@plurnk/plurnk-service 1.0.4 → 1.0.6

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 (215) hide show
  1. package/.env.defaults +55 -31
  2. package/INSTALL.md +3 -3
  3. package/README.md +4 -4
  4. package/SPEC.md +205 -193
  5. package/dist/Paths.js +1 -1
  6. package/dist/Paths.js.map +1 -1
  7. package/dist/core/ChannelWrite.d.ts +18 -18
  8. package/dist/core/ChannelWrite.d.ts.map +1 -1
  9. package/dist/core/ChannelWrite.js +17 -17
  10. package/dist/core/ChannelWrite.js.map +1 -1
  11. package/dist/core/ChannelWrite.sql +11 -11
  12. package/dist/core/Dispatcher.d.ts +11 -16
  13. package/dist/core/Dispatcher.d.ts.map +1 -1
  14. package/dist/core/Dispatcher.js +122 -125
  15. package/dist/core/Dispatcher.js.map +1 -1
  16. package/dist/core/Engine.d.ts +20 -23
  17. package/dist/core/Engine.d.ts.map +1 -1
  18. package/dist/core/Engine.js +213 -210
  19. package/dist/core/Engine.js.map +1 -1
  20. package/dist/core/Engine.sql +108 -130
  21. package/dist/core/PacketBuilder.d.ts +10 -11
  22. package/dist/core/PacketBuilder.d.ts.map +1 -1
  23. package/dist/core/PacketBuilder.js +204 -128
  24. package/dist/core/PacketBuilder.js.map +1 -1
  25. package/dist/core/ProposalLifecycle.d.ts +8 -8
  26. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  27. package/dist/core/ProposalLifecycle.js +10 -10
  28. package/dist/core/ProposalLifecycle.js.map +1 -1
  29. package/dist/core/ProviderInstantiate.d.ts +1 -0
  30. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  31. package/dist/core/ProviderInstantiate.js +9 -3
  32. package/dist/core/ProviderInstantiate.js.map +1 -1
  33. package/dist/core/SchemeRegistry.d.ts +4 -1
  34. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  35. package/dist/core/SchemeRegistry.js +18 -9
  36. package/dist/core/SchemeRegistry.js.map +1 -1
  37. package/dist/core/TelemetryChannel.d.ts +5 -5
  38. package/dist/core/TelemetryChannel.d.ts.map +1 -1
  39. package/dist/core/TelemetryChannel.js +9 -9
  40. package/dist/core/TelemetryChannel.js.map +1 -1
  41. package/dist/core/caps/CapsResolve.js +2 -2
  42. package/dist/core/caps/CapsResolve.js.map +1 -1
  43. package/dist/core/caps/DbNotifyCaps.js +2 -2
  44. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  45. package/dist/core/caps/DbSubscriptionCaps.js +7 -7
  46. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  47. package/dist/core/caps/SchemeCtxImpl.d.ts +2 -2
  48. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  49. package/dist/core/caps/SchemeCtxImpl.js +4 -4
  50. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  51. package/dist/core/content-hash.d.ts +2 -0
  52. package/dist/core/content-hash.d.ts.map +1 -0
  53. package/dist/core/content-hash.js +6 -0
  54. package/dist/core/content-hash.js.map +1 -0
  55. package/dist/core/fork.d.ts +1 -1
  56. package/dist/core/fork.d.ts.map +1 -1
  57. package/dist/core/fork.js +31 -26
  58. package/dist/core/fork.js.map +1 -1
  59. package/dist/core/fork.sql +41 -32
  60. package/dist/core/git-env.d.ts.map +1 -1
  61. package/dist/core/git-env.js +23 -7
  62. package/dist/core/git-env.js.map +1 -1
  63. package/dist/core/git-iso.d.ts +9 -0
  64. package/dist/core/git-iso.d.ts.map +1 -0
  65. package/dist/core/git-iso.js +227 -0
  66. package/dist/core/git-iso.js.map +1 -0
  67. package/dist/core/git-membership.d.ts +2 -2
  68. package/dist/core/git-membership.d.ts.map +1 -1
  69. package/dist/core/git-membership.js +70 -50
  70. package/dist/core/git-membership.js.map +1 -1
  71. package/dist/core/git-state.d.ts +1 -1
  72. package/dist/core/git-state.d.ts.map +1 -1
  73. package/dist/core/git-state.js +21 -11
  74. package/dist/core/git-state.js.map +1 -1
  75. package/dist/core/packet-wire.d.ts.map +1 -1
  76. package/dist/core/packet-wire.js +81 -22
  77. package/dist/core/packet-wire.js.map +1 -1
  78. package/dist/core/plurnk-uri.d.ts +2 -2
  79. package/dist/core/plurnk-uri.d.ts.map +1 -1
  80. package/dist/core/plurnk-uri.js +19 -12
  81. package/dist/core/plurnk-uri.js.map +1 -1
  82. package/dist/core/run-ops.sql +26 -26
  83. package/dist/core/scheme-types.d.ts +6 -11
  84. package/dist/core/scheme-types.d.ts.map +1 -1
  85. package/dist/core/search-gate.js +0 -0
  86. package/dist/core/search-gate.js.map +1 -1
  87. package/dist/core/token-ruler.d.ts +2 -0
  88. package/dist/core/token-ruler.d.ts.map +1 -0
  89. package/dist/core/token-ruler.js +18 -0
  90. package/dist/core/token-ruler.js.map +1 -0
  91. package/dist/core/worker-cap.d.ts +8 -0
  92. package/dist/core/worker-cap.d.ts.map +1 -0
  93. package/dist/core/worker-cap.js +20 -0
  94. package/dist/core/worker-cap.js.map +1 -0
  95. package/dist/core/{session-settings.d.ts → workspace-settings.d.ts} +5 -5
  96. package/dist/core/workspace-settings.d.ts.map +1 -0
  97. package/dist/core/{session-settings.js → workspace-settings.js} +13 -13
  98. package/dist/core/workspace-settings.js.map +1 -0
  99. package/dist/digest/Digest.d.ts +3 -3
  100. package/dist/digest/Digest.d.ts.map +1 -1
  101. package/dist/digest/Digest.js +79 -73
  102. package/dist/digest/Digest.js.map +1 -1
  103. package/dist/digest/digest.sql +18 -18
  104. package/dist/schemes/Exec.d.ts +9 -2
  105. package/dist/schemes/Exec.d.ts.map +1 -1
  106. package/dist/schemes/Exec.js +121 -68
  107. package/dist/schemes/Exec.js.map +1 -1
  108. package/dist/schemes/ExecOutputScheme.js +3 -3
  109. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  110. package/dist/schemes/File.js +23 -23
  111. package/dist/schemes/File.js.map +1 -1
  112. package/dist/schemes/Known.js +5 -5
  113. package/dist/schemes/Known.js.map +1 -1
  114. package/dist/schemes/Log.d.ts.map +1 -1
  115. package/dist/schemes/Log.js +63 -14
  116. package/dist/schemes/Log.js.map +1 -1
  117. package/dist/schemes/Log.sql +49 -8
  118. package/dist/schemes/Plurnk.js +5 -5
  119. package/dist/schemes/Plurnk.js.map +1 -1
  120. package/dist/schemes/Skill.js +5 -5
  121. package/dist/schemes/Skill.js.map +1 -1
  122. package/dist/schemes/Unknown.js +5 -5
  123. package/dist/schemes/Unknown.js.map +1 -1
  124. package/dist/schemes/{Run.d.ts → Worker.d.ts} +2 -2
  125. package/dist/schemes/Worker.d.ts.map +1 -0
  126. package/dist/schemes/{Run.js → Worker.js} +67 -67
  127. package/dist/schemes/Worker.js.map +1 -0
  128. package/dist/schemes/_entry-crud.js +11 -11
  129. package/dist/schemes/_entry-crud.js.map +1 -1
  130. package/dist/schemes/_entry-crud.sql +29 -29
  131. package/dist/schemes/_entry-find.d.ts +2 -1
  132. package/dist/schemes/_entry-find.d.ts.map +1 -1
  133. package/dist/schemes/_entry-find.js +31 -19
  134. package/dist/schemes/_entry-find.js.map +1 -1
  135. package/dist/schemes/_entry-find.sql +9 -9
  136. package/dist/schemes/_entry-graph.d.ts +2 -2
  137. package/dist/schemes/_entry-graph.d.ts.map +1 -1
  138. package/dist/schemes/_entry-graph.js +18 -18
  139. package/dist/schemes/_entry-graph.js.map +1 -1
  140. package/dist/schemes/_entry-graph.sql +8 -8
  141. package/dist/schemes/_entry-manifest.d.ts +1 -1
  142. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  143. package/dist/schemes/_entry-manifest.js +74 -50
  144. package/dist/schemes/_entry-manifest.js.map +1 -1
  145. package/dist/schemes/_entry-ops.d.ts +4 -4
  146. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  147. package/dist/schemes/_entry-ops.js +25 -25
  148. package/dist/schemes/_entry-ops.js.map +1 -1
  149. package/dist/schemes/_entry-ops.sql +9 -9
  150. package/dist/schemes/_entry-semantic.d.ts +2 -1
  151. package/dist/schemes/_entry-semantic.d.ts.map +1 -1
  152. package/dist/schemes/_entry-semantic.js +23 -4
  153. package/dist/schemes/_entry-semantic.js.map +1 -1
  154. package/dist/schemes/_entry-semantic.sql +18 -4
  155. package/dist/schemes/_entry-send.d.ts +1 -1
  156. package/dist/schemes/_entry-send.d.ts.map +1 -1
  157. package/dist/schemes/_entry-send.js +6 -6
  158. package/dist/schemes/_entry-send.js.map +1 -1
  159. package/dist/schemes/exec-abort.js +1 -1
  160. package/dist/schemes/exec-abort.js.map +1 -1
  161. package/dist/server/Daemon.d.ts +50 -48
  162. package/dist/server/Daemon.d.ts.map +1 -1
  163. package/dist/server/Daemon.js +307 -297
  164. package/dist/server/Daemon.js.map +1 -1
  165. package/dist/server/client-input.js +20 -20
  166. package/dist/server/client-input.js.map +1 -1
  167. package/dist/server/dispatch-as-plurnk.d.ts +1 -1
  168. package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
  169. package/dist/server/dispatch-as-plurnk.js +7 -7
  170. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  171. package/dist/server/drain.sql +27 -27
  172. package/dist/server/envelope.d.ts +20 -20
  173. package/dist/server/envelope.d.ts.map +1 -1
  174. package/dist/server/envelope.js +86 -86
  175. package/dist/server/envelope.js.map +1 -1
  176. package/dist/server/envelope.sql +39 -39
  177. package/dist/server/logEntry.d.ts +1 -1
  178. package/dist/server/logEntry.d.ts.map +1 -1
  179. package/dist/server/logEntry.js +1 -1
  180. package/dist/server/logEntry.js.map +1 -1
  181. package/dist/server/loop-model.d.ts +3 -0
  182. package/dist/server/loop-model.d.ts.map +1 -0
  183. package/dist/server/loop-model.js +26 -0
  184. package/dist/server/loop-model.js.map +1 -0
  185. package/dist/server/loopDocs.d.ts +1 -1
  186. package/dist/server/loopDocs.d.ts.map +1 -1
  187. package/dist/server/loopDocs.js +6 -6
  188. package/dist/server/loopDocs.js.map +1 -1
  189. package/dist/server/seam-entry-read.sql +2 -2
  190. package/dist/server/seam-log-read.sql +3 -3
  191. package/dist/server/seam-loop.sql +3 -3
  192. package/dist/server/seam-proposal-list.sql +4 -4
  193. package/dist/server/yolo.js +2 -2
  194. package/dist/server/yolo.js.map +1 -1
  195. package/dist/service.d.ts.map +1 -1
  196. package/dist/service.js +21 -6
  197. package/dist/service.js.map +1 -1
  198. package/migrations/0000-00-00.01_schema.sql +84 -85
  199. package/package.json +17 -14
  200. package/dist/core/TokenGauge.d.ts +0 -16
  201. package/dist/core/TokenGauge.d.ts.map +0 -1
  202. package/dist/core/TokenGauge.js +0 -58
  203. package/dist/core/TokenGauge.js.map +0 -1
  204. package/dist/core/run-cap.d.ts +0 -8
  205. package/dist/core/run-cap.d.ts.map +0 -1
  206. package/dist/core/run-cap.js +0 -20
  207. package/dist/core/run-cap.js.map +0 -1
  208. package/dist/core/search-prefetch.d.ts +0 -17
  209. package/dist/core/search-prefetch.d.ts.map +0 -1
  210. package/dist/core/search-prefetch.js +0 -164
  211. package/dist/core/search-prefetch.js.map +0 -1
  212. package/dist/core/session-settings.d.ts.map +0 -1
  213. package/dist/core/session-settings.js.map +0 -1
  214. package/dist/schemes/Run.d.ts.map +0 -1
  215. package/dist/schemes/Run.js.map +0 -1
@@ -2,13 +2,13 @@ var _a;
2
2
  import { parsePath } from "@plurnk/plurnk-grammar";
3
3
  import { foldAuthorityIntoPath, schemeNameOf } from "./plurnk-uri.js";
4
4
  import Fork from "./fork.js";
5
- import RunCap from "./run-cap.js";
5
+ import WorkerCap from "./worker-cap.js";
6
6
  import { decodePathParens } from "./path-decode.js";
7
7
  import { DEFAULT_LOOP_FLAGS } from "./scheme-types.js";
8
8
  import { LineMarkerOps, MimetypeBinary } from "../content/index.js";
9
9
  import SchemeCtxImpl from "./caps/SchemeCtxImpl.js";
10
10
  import EntryCrud from "../schemes/_entry-crud.js";
11
- import SessionSettings from "./session-settings.js";
11
+ import WorkspaceSettings from "./workspace-settings.js";
12
12
  // SPEC §scheme-surface: writer must be in target scheme's manifest.writableBy.
13
13
  // OPEN/FOLD/READ/FIND are not gated — they curate the log or read, never mutating an entry.
14
14
  const MUTATING_OPS = new Set(["EDIT", "SEND", "COPY", "MOVE", "EXEC", "KILL", "FORK", "WORK"]);
@@ -32,17 +32,17 @@ class Dispatcher {
32
32
  // Per-loop abort signal, owned by Engine.runLoop — thunked.
33
33
  #loopSignal;
34
34
  #streamEventNotify;
35
- #wakeRunNotify;
36
- #injectRun;
37
- #cancelRun;
35
+ #wakeWorkerNotify;
36
+ #injectWorker;
37
+ #cancelWorker;
38
38
  // §send-premature-terminate/[102]<T> — the engine-owned park-deadline registry (loopId → seconds;
39
39
  // -1 = indefinite). The dispatcher WRITES at park; the daemon's drain park-exit consumes.
40
40
  #parkDeadlines;
41
41
  #searchGate;
42
- // §join-blocking-collect (#354) — loops with a READ(run://running-child) armed this turn; a bare
42
+ // §join-blocking-collect (#354) — loops with a READ(worker://running-child) armed this turn; a bare
43
43
  // SEND[102] parks on it (blocking join). Engine-owned, twin of #parkDeadlines.
44
44
  #joinTargets;
45
- constructor({ db, schemes, mimetypes, tokenize, telemetry, proposals, executors, loopSignal, streamEventNotify, wakeRunNotify, injectRun, cancelRun, searchGate, parkDeadlines, joinTargets }) {
45
+ constructor({ db, schemes, mimetypes, tokenize, telemetry, proposals, executors, loopSignal, streamEventNotify, wakeWorkerNotify, injectWorker, cancelWorker, searchGate, parkDeadlines, joinTargets }) {
46
46
  this.#db = db;
47
47
  this.#schemes = schemes;
48
48
  this.#mimetypes = mimetypes;
@@ -52,16 +52,16 @@ class Dispatcher {
52
52
  this.#executors = executors;
53
53
  this.#loopSignal = loopSignal;
54
54
  this.#streamEventNotify = streamEventNotify;
55
- this.#wakeRunNotify = wakeRunNotify;
56
- this.#injectRun = injectRun;
57
- this.#cancelRun = cancelRun;
55
+ this.#wakeWorkerNotify = wakeWorkerNotify;
56
+ this.#injectWorker = injectWorker;
57
+ this.#cancelWorker = cancelWorker;
58
58
  this.#parkDeadlines = parkDeadlines ?? new Map();
59
59
  this.#searchGate = searchGate;
60
60
  this.#joinTargets = joinTargets ?? new Set();
61
61
  }
62
62
  async dispatch(context) {
63
- const { statement, sessionId, runId, loopId, turnId, sequence, origin, onDispatch, gauge, turnParseErrors } = context;
64
- const schemeCtx = this.#buildSchemeCtx({ sessionId, runId, loopId, turnId, origin, gauge });
63
+ const { statement, workspaceId, workerId, loopId, turnId, sequence, origin, onDispatch, turnParseErrors } = context;
64
+ const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, loopId, turnId, origin });
65
65
  let result;
66
66
  let denial = this.#checkWritable(statement, origin);
67
67
  if (denial === null)
@@ -73,7 +73,7 @@ class Dispatcher {
73
73
  // READ honors FIND: a glob/folder scope or a matcher fans out to one log row per MATCH
74
74
  // (its own writeLogs), returning early. A READ never proposes, so it bypasses the
75
75
  // single-row path below. A bare entry, body-less, falls through to the direct read. #286
76
- return await this.#handleReadFanout(statement, schemeCtx, { runId, loopId, turnId, sequence, origin, onDispatch });
76
+ return await this.#handleReadFanout(statement, schemeCtx, { workerId, loopId, turnId, sequence, origin, onDispatch });
77
77
  }
78
78
  else {
79
79
  // SPEC §scheme-surface + plurnk-schemes#1: action-entry-as-outcome. Scheme-handler
@@ -83,10 +83,10 @@ class Dispatcher {
83
83
  // those are system failures.
84
84
  try {
85
85
  if (statement.op === "SEND" && statement.target === null) {
86
- result = await this.#handleSendBroadcast(statement, { sessionId, runId, loopId, turnId, turnParseErrors });
86
+ result = await this.#handleSendBroadcast(statement, { workspaceId, workerId, loopId, turnId, turnParseErrors });
87
87
  }
88
88
  else if (statement.op === "FORK" || statement.op === "WORK") {
89
- result = await this.#handleRunControl(statement, schemeCtx);
89
+ result = await this.#handleWorkerControl(statement, schemeCtx);
90
90
  }
91
91
  else if (statement.op === "COPY") {
92
92
  result = await this.#handleCopy(statement, schemeCtx);
@@ -122,11 +122,11 @@ class Dispatcher {
122
122
  // recorded (#382 — a curation act with no forensic trace is how run43's task-frame fold
123
123
  // stayed invisible), but engine_render_log suppresses them from the packet, so the receipt
124
124
  // rents zero model-facing space — the original clutter concern, resolved by hide-not-drop.
125
- // §join-blocking-collect (#354) — Run.read on a still-running child returns an awaitRun signal;
125
+ // §join-blocking-collect (#354) — Run.read on a still-running child returns an awaitWorker signal;
126
126
  // arm the join so THIS turn's bare SEND[102] parks (the blocking collect) instead of spinning.
127
- if (typeof result.awaitRun === "string")
127
+ if (typeof result.awaitWorker === "string")
128
128
  this.#joinTargets.add(loopId);
129
- const logEntryId = await this.#writeLog({ statement, result, runId, loopId, turnId, sequence, origin });
129
+ const logEntryId = await this.#writeLog({ statement, result, workerId, loopId, turnId, sequence, origin });
130
130
  // §search-gate — register successful searches AFTER #writeLog stamps the runtime
131
131
  // entry's coordinate onto result.attrs.pathname (the gate's dedup serves from it).
132
132
  if (statement.op === "EXEC" && result.status < 400) {
@@ -149,7 +149,7 @@ class Dispatcher {
149
149
  // no human gate, no loop/proposal notification. Accept + apply
150
150
  // in-process; the model sees the outcome directly, never a review.
151
151
  if (result.attrs?.inline === true) {
152
- const effective = await this.#proposals.runApply(statement, result, { decision: "accept" }, { sessionId, runId, loopId, turnId });
152
+ const effective = await this.#proposals.workerApply(statement, result, { decision: "accept" }, { workspaceId, workerId, loopId, turnId });
153
153
  return this.#proposals.applyResolution(logEntryId, effective);
154
154
  }
155
155
  // Register the resolution waiter SYNCHRONOUSLY before any await
@@ -165,9 +165,9 @@ class Dispatcher {
165
165
  const flags = await this.#loadLoopFlags(loopId); // the loop/proposal notification carries flags (yolo) — §dual-yolo-proposal-carries-flags
166
166
  // #note10 — if the target diverged on disk this turn, the model's EDIT is based
167
167
  // on a stale read; flag it so a YOLO auto-accept rejects instead of clobbering.
168
- const diverged = await this.#db.engine_target_diverged_this_turn.get({ run_id: runId, turn_id: turnId, scheme: target.scheme, pathname: target.pathname });
168
+ const diverged = await this.#db.engine_target_diverged_this_turn.get({ worker_id: workerId, turn_id: turnId, scheme: target.scheme, pathname: target.pathname });
169
169
  const event = {
170
- logEntryId, sessionId, runId, loopId, turnId,
170
+ logEntryId, workspaceId, workerId, loopId, turnId,
171
171
  op: statement.op,
172
172
  target: { scheme: target.scheme, pathname: target.pathname },
173
173
  body: typeof result.body === "string" ? result.body : "",
@@ -182,7 +182,7 @@ class Dispatcher {
182
182
  // 4xx/5xx or throws, the resolution is downgraded to a reject
183
183
  // with the failure outcome — engine treats it like a client
184
184
  // rejection.
185
- const effective = await this.#proposals.runApply(statement, result, resolution, { sessionId, runId, loopId, turnId });
185
+ const effective = await this.#proposals.workerApply(statement, result, resolution, { workspaceId, workerId, loopId, turnId });
186
186
  // MOVE into a proposed dest: the deferred source-delete fires ONLY now,
187
187
  // after the dest write landed (accept). On reject the source survives.
188
188
  if (effective.decision === "accept") {
@@ -209,30 +209,29 @@ class Dispatcher {
209
209
  // dispatch's resolve path minus #writeLog. Runs on the client loop, so the
210
210
  // human's inspection is never constrained by a model loop's flags. {§op-look}
211
211
  async look(context) {
212
- const { statement, sessionId, runId, loopId, origin = "client" } = context;
212
+ const { statement, workspaceId, workerId, loopId, origin = "client" } = context;
213
213
  if (statement.op !== "READ")
214
214
  throw new Error(`look resolves READ only; got ${statement.op}`);
215
215
  // turnId is a write-time FK only — a look writes no row, so 0 (no turn) is inert.
216
- const schemeCtx = this.#buildSchemeCtx({ sessionId, runId, loopId, turnId: 0, origin });
216
+ const schemeCtx = this.#buildSchemeCtx({ workspaceId, workerId, loopId, turnId: 0, origin });
217
217
  const denial = await this.#checkFlagsGate(statement, loopId);
218
218
  if (denial !== null)
219
219
  return denial;
220
220
  return this.#run(schemeNameOf(statement.target), statement, schemeCtx);
221
221
  }
222
222
  #buildSchemeCtx(ids) {
223
- const { sessionId, runId, loopId, turnId, origin, gauge } = ids;
223
+ const { workspaceId, workerId, loopId, turnId, origin } = ids;
224
224
  return {
225
225
  db: this.#db,
226
- sessionId, runId, loopId, turnId,
226
+ workspaceId, workerId, loopId, turnId,
227
227
  writer: origin,
228
228
  signal: this.#loopSignal(loopId),
229
229
  streamEventNotify: this.#streamEventNotify,
230
- wakeRunNotify: this.#wakeRunNotify,
231
- injectRun: this.#injectRun,
230
+ wakeWorkerNotify: this.#wakeWorkerNotify,
231
+ injectWorker: this.#injectWorker,
232
232
  mimetypes: this.#mimetypes,
233
233
  tokenize: this.#tokenize,
234
- gauge,
235
- pushTelemetry: (event) => this.#telemetry.push(sessionId, loopId, event),
234
+ pushTelemetry: (event) => this.#telemetry.push(workspaceId, loopId, event),
236
235
  executors: this.#executors(),
237
236
  };
238
237
  }
@@ -269,11 +268,11 @@ class Dispatcher {
269
268
  if (statement.op === "EXEC") {
270
269
  return this.#denyIfDisallowed("exec", origin);
271
270
  }
272
- // Run control (FORK/WORK → run://<name>, spawn or fork) is gated by run://'s writableBy — its
271
+ // Run control (FORK/WORK → worker://<name>, spawn or fork) is gated by worker://'s writableBy — its
273
272
  // body is a seed prompt, not a dst path, so the entry-COPY dst-parse below doesn't apply.
274
273
  // §machine-processes
275
- if (this.#isRunControl(statement))
276
- return this.#denyIfDisallowed("run", origin);
274
+ if (this.#isWorkerControl(statement))
275
+ return this.#denyIfDisallowed("worker", origin);
277
276
  if (statement.op === "COPY" || statement.op === "MOVE") {
278
277
  const dst = statement.op === "COPY" ? (statement.body === null ? null : parsePath(statement.body)) : statement.body;
279
278
  const dstScheme = schemeNameOf(dst);
@@ -303,7 +302,7 @@ class Dispatcher {
303
302
  const command = ("body" in statement && typeof statement.body === "string") ? statement.body : "";
304
303
  const verdict = this.#searchGate?.check(loopId, turnId, runtime, command) ?? { verdict: "pass" };
305
304
  if (verdict.verdict === "capped") {
306
- return { status: 429, error: `Per-turn search limit reached (${verdict.cap}) — act on the results you already hold; further searches continue next turn.` };
305
+ return { status: 429, error: `Per-turn search limit reached (${verdict.cap}).` };
307
306
  }
308
307
  if (verdict.verdict === "duplicate") {
309
308
  const prior = await EntryCrud.readEntry(verdict.priorPathname, ctx, runtime);
@@ -381,7 +380,7 @@ class Dispatcher {
381
380
  return null;
382
381
  return { status: 403, error: `'${scheme}' is unavailable: ${restriction}. Do NOT retry it — it is unavailable, not failing; answer or advise the user directly (an act-mode loop is required to use it).` };
383
382
  };
384
- if (this.#isRunControl(statement))
383
+ if (this.#isWorkerControl(statement))
385
384
  return check(statement.target); // body is a spawn/fork task, not a dst path
386
385
  if (statement.op === "COPY" || statement.op === "MOVE") {
387
386
  return check(statement.target) ?? check(statement.op === "COPY" ? (statement.body === null ? null : parsePath(statement.body)) : statement.body);
@@ -389,59 +388,59 @@ class Dispatcher {
389
388
  return check(statement.target);
390
389
  }
391
390
  // Run control is FORK/WORK (grammar 0.74.55), not COPY — its body
392
- // is the new run's seed prompt, not a destination path. The COPY gates and #handleCopy
391
+ // is the new worker's seed prompt, not a destination path. The COPY gates and #handleCopy
393
392
  // branch on this so they never parse the prompt as a dst path.
394
- #isRunControl(statement) {
395
- return statement.op === "FORK" || statement.op === "WORK"; // run control targets run://<name> (grammar 0.74.55)
393
+ #isWorkerControl(statement) {
394
+ return statement.op === "FORK" || statement.op === "WORK"; // run control targets worker://<name> (grammar 0.74.55)
396
395
  }
397
- // FORK/WORK(run://<name>):task — run control (grammar 0.74.55):
398
- // • run://self → FORK: deep-copy the current run's log into a new sister (Fork), then
396
+ // FORK/WORK(worker://<name>):task — run control (grammar 0.74.55):
397
+ // • worker://self → FORK: deep-copy the current worker's log into a new sister (Fork), then
399
398
  // continue it with the prompt (§machine-processes-fork-copies-the-log).
400
- // • run://<name> → SPAWN: a fresh sister (empty log) named <name>, started on the prompt.
399
+ // • worker://<name> → SPAWN: a fresh sister (empty log) named <name>, started on the prompt.
401
400
  // A LIVE sister already holding <name> is a 409 conflict; a free or terminated name is
402
- // reclaimed (§run-scheme-spawn). The self form is fork; only a name spawns.
403
- // Both ride the daemon inject and obey the active-runs cap (508, §run-scheme-cap).
401
+ // reclaimed (§worker-scheme-spawn). The self form is fork; only a name spawns.
402
+ // Both ride the daemon inject and obey the active-runs cap (508, §worker-scheme-cap).
404
403
  // FORK/WORK — run control (grammar 0.74.55). Both name a NEW run in the target authority
405
- // (run://<name>) and carry its seed task in the body. WORK spawns a fresh worker; FORK branches
406
- // the current run's log into a named sister. Replaces the COPY(run://) overload — one verb, one
404
+ // (worker://<name>) and carry its seed task in the body. WORK spawns a fresh worker; FORK branches
405
+ // the current worker's log into a named sister. Replaces the COPY(worker://) overload — one verb, one
407
406
  // intent, so the model never conflates the target slot with the body (grammar#52).
408
- async #handleRunControl(statement, ctx) {
407
+ async #handleWorkerControl(statement, ctx) {
409
408
  const target = statement.target;
410
409
  if (target === null)
411
- return { status: 400, error: `${statement.op} requires a run target (${statement.op}(run://<name>))` };
412
- const name = target.kind === "url" ? (target.hostname ?? "") : ""; // §run-scheme — run is the AUTHORITY (run://<name>), not the path
410
+ return { status: 400, error: `${statement.op} requires a worker target (${statement.op}(worker://<name>))` };
411
+ const name = target.kind === "url" ? (target.hostname ?? "") : ""; // §worker-scheme — run is the AUTHORITY (worker://<name>), not the path
413
412
  if (name === "")
414
- return { status: 400, error: `${statement.op} requires a run name (run://<name>)` };
413
+ return { status: 400, error: `${statement.op} requires a worker name (worker://<name>)` };
415
414
  if (name === "self")
416
- return { status: 400, error: `'self' is the current run — ${statement.op} names a NEW run (run://<name>)` };
417
- if (ctx.injectRun === undefined)
418
- throw new Error("run control: injectRun capability absent");
419
- const denied = await RunCap.deny(this.#db, ctx.sessionId);
415
+ return { status: 400, error: `'self' is the current run — ${statement.op} names a NEW run (worker://<name>)` };
416
+ if (ctx.injectWorker === undefined)
417
+ throw new Error("run control: injectWorker capability absent");
418
+ const denied = await WorkerCap.deny(this.#db, ctx.workspaceId);
420
419
  if (denied !== null)
421
420
  return denied;
422
421
  const prompt = typeof statement.body === "string" ? statement.body : "";
423
- // §run-delegation-inherits-flags — authority flows down the delegation edge: the child's live
422
+ // §worker-delegation-inherits-flags — authority flows down the delegation edge: the child's live
424
423
  // loop runs with ITS DELEGATOR'S flags. A flagless (non-YOLO) child's every side-effecting op
425
424
  // proposes into a resolver-less void — 300s auto-cancel per attempt was the fan-out wedge.
426
425
  const flags = await this.#loadLoopFlags(ctx.loopId);
427
- // A name is frozen per run but reclaimable across time (§machine-processes-run-origin): a LIVE
426
+ // A name is frozen per worker but reclaimable across time (§machine-processes-worker-origin): a LIVE
428
427
  // sister holding it is a 409 (legible, never a raw UNIQUE 500); a free/terminated name reclaims.
429
- const live = await this.#db.run_live_by_name.get({ session_id: ctx.sessionId, name });
428
+ const live = await this.#db.worker_live_by_name.get({ workspace_id: ctx.workspaceId, name });
430
429
  if (live !== undefined)
431
- return { status: 409, error: `run '${name}' is already running` };
430
+ return { status: 409, error: `worker '${name}' is already running` };
432
431
  if (statement.op === "FORK") {
433
- // Branch the current run's log into a named sister.
434
- const branchRunId = await Fork.fork(this.#db, ctx.runId, name);
435
- await ctx.injectRun({ sessionId: ctx.sessionId, runId: branchRunId, prompt, flags });
432
+ // Branch the current worker's log into a named sister.
433
+ const branchWorkerId = await Fork.fork(this.#db, ctx.workerId, name);
434
+ await ctx.injectWorker({ workspaceId: ctx.workspaceId, workerId: branchWorkerId, prompt, flags });
436
435
  return { status: 200, body: name };
437
436
  }
438
437
  // WORK — a fresh worker sister named <name>.
439
- const row = await this.#db.fork_insert_run.get({
440
- session_id: ctx.sessionId, name, parent_run_id: ctx.runId, origin: ctx.writer,
438
+ const row = await this.#db.fork_insert_worker.get({
439
+ workspace_id: ctx.workspaceId, name, parent_worker_id: ctx.workerId, origin: ctx.writer,
441
440
  });
442
441
  if (row === undefined)
443
442
  throw new Error("run spawn: run insert returned no row");
444
- await ctx.injectRun({ sessionId: ctx.sessionId, runId: row.id, prompt, flags });
443
+ await ctx.injectWorker({ workspaceId: ctx.workspaceId, workerId: row.id, prompt, flags });
445
444
  return { status: 200, body: name };
446
445
  }
447
446
  async #handleCopy(statement, ctx) {
@@ -519,37 +518,37 @@ class Dispatcher {
519
518
  if (killable !== undefined && typeof killable.kill === "function") {
520
519
  return await killable.kill(pathnameFromPath(path), statement.signal, ctx);
521
520
  }
522
- if (schemeName === "run") {
523
- // Entry-path present → KILL a run-scope scratch ENTRY (delete it), self-only —
521
+ if (schemeName === "worker") {
522
+ // Entry-path present → KILL a worker-scope scratch ENTRY (delete it), self-only —
524
523
  // NOT run cancellation. The authority (hostname) names the owner, the pathname the
525
- // entry; only the path-ABSENT form (run://<name>) terminates the run-as-actor. §run-scheme
524
+ // entry; only the path-ABSENT form (worker://<name>) terminates the run-as-actor. §worker-scheme
526
525
  const entryPath = path.kind === "url" ? (path.pathname ?? "") : "";
527
526
  if (entryPath !== "" && entryPath !== "/") {
528
- const runHandler = this.#schemes.get("run");
529
- return await runHandler.deleteEntry(statement, ctx);
527
+ const workerHandler = this.#schemes.get("worker");
528
+ return await workerHandler.deleteEntry(statement, ctx);
530
529
  }
531
- // terminate — abort any run by address; whoever holds it may end it.
532
- // `run://self` = self. cancelRun (→ Daemon.cancelDrain) aborts the run's signal
530
+ // terminate — abort any worker by address; whoever holds it may end it.
531
+ // `worker://self` = self. cancelWorker (→ Daemon.cancelDrain) aborts the worker's signal
533
532
  // (its loop closes 499); an idle run is a no-op-200, a missing run 404.
534
- const name = path.kind === "url" ? (path.hostname ?? "") : ""; // §run-scheme — run is the AUTHORITY
533
+ const name = path.kind === "url" ? (path.hostname ?? "") : ""; // §worker-scheme — run is the AUTHORITY
535
534
  if (name === "")
536
- return { status: 400, error: "run:// kill requires a run name or 'self' (run://<name>)" };
537
- let runId = ctx.runId;
535
+ return { status: 400, error: "worker:// kill requires a worker name or 'self' (worker://<name>)" };
536
+ let workerId = ctx.workerId;
538
537
  if (name !== "self") {
539
- const row = await this.#db.run_resolve_by_name.get({ session_id: ctx.sessionId, name });
538
+ const row = await this.#db.worker_resolve_by_name.get({ workspace_id: ctx.workspaceId, name });
540
539
  if (row === undefined)
541
- return { status: 404, error: `run://${name} not found in this session` };
542
- runId = row.id;
540
+ return { status: 404, error: `worker://${name} not found in this workspace` };
541
+ workerId = row.id;
543
542
  }
544
- if (this.#cancelRun === undefined)
545
- throw new Error("run kill: cancelRun capability absent");
546
- // §op-synchronous — KILL is DECISIVE: flip the run's live loops to 499 NOW so the
543
+ if (this.#cancelWorker === undefined)
544
+ throw new Error("run kill: cancelWorker capability absent");
545
+ // §op-synchronous — KILL is DECISIVE: flip the worker's live loops to 499 NOW so the
547
546
  // same-turn premature-terminate gate sees it dead (KILL … SEND[200] concludes in ONE
548
547
  // turn — the model never reasons about async reap timing). The physical scope reap
549
- // (drain abort + stream teardown) then rides cancelRun; a killed loop can't heal back
550
- // because cancelRun aborts its drain's signal.
551
- await this.#db.engine_terminate_run_live_loops.run({ run_id: runId, message: "killed via run:// KILL" });
552
- this.#cancelRun(runId);
548
+ // (drain abort + stream teardown) then rides cancelWorker; a killed loop can't heal back
549
+ // because cancelWorker aborts its drain's signal.
550
+ await this.#db.engine_terminate_worker_live_loops.run({ worker_id: workerId, message: "killed via worker:// KILL" });
551
+ this.#cancelWorker(workerId);
553
552
  return { status: 200 };
554
553
  }
555
554
  const handler = this.#schemes.get(schemeName);
@@ -590,7 +589,7 @@ class Dispatcher {
590
589
  return { ...statement, target: target, lineMarker, body: null };
591
590
  }
592
591
  async #handleReadFanout(statement, ctx, ids) {
593
- const { runId, loopId, turnId, sequence, origin, onDispatch } = ids;
592
+ const { workerId, loopId, turnId, sequence, origin, onDispatch } = ids;
594
593
  const schemeName = schemeNameOf(statement.target);
595
594
  const found = await this.#run(schemeName, { ...statement, op: "FIND" }, ctx);
596
595
  const matches = found.matches ?? [];
@@ -598,7 +597,7 @@ class Dispatcher {
598
597
  // status, exactly like a non-fanned READ. The model sees the empty/failed result, not silence.
599
598
  if (found.status !== 200 || matches.length === 0) {
600
599
  const result = { status: found.status === 200 ? 204 : found.status };
601
- const id = await this.#writeLog({ statement, result, runId, loopId, turnId, sequence, origin });
600
+ const id = await this.#writeLog({ statement, result, workerId, loopId, turnId, sequence, origin });
602
601
  onDispatch?.(id);
603
602
  return { ...result, rowsWritten: 1 };
604
603
  }
@@ -612,7 +611,7 @@ class Dispatcher {
612
611
  // had FINDed then READ. On a degenerate single-line document the N delivery rows are
613
612
  // identical whole-file lines; the summary row is what tells the model its query hit N
614
613
  // times and WHERE (run30: two hits indistinguishable from failure; 17 retries, 508).
615
- const findRowId = await this.#writeLog({ statement: { ...statement, op: "FIND" }, result: found, runId, loopId, turnId, sequence, origin });
614
+ const findRowId = await this.#writeLog({ statement: { ...statement, op: "FIND" }, result: found, workerId, loopId, turnId, sequence, origin });
616
615
  onDispatch?.(findRowId);
617
616
  const wholeByPath = new Map();
618
617
  const fannedStatuses = [];
@@ -624,7 +623,7 @@ class Dispatcher {
624
623
  wholeByPath.set(m.pathname, whole);
625
624
  }
626
625
  const result = _a.#sliceMatch(whole, m.span);
627
- const id = await this.#writeLog({ statement: _a.#retargetRead(statement, m.pathname, m.span), result, runId, loopId, turnId, sequence: sequence + written, origin });
626
+ const id = await this.#writeLog({ statement: _a.#retargetRead(statement, m.pathname, m.span), result, workerId, loopId, turnId, sequence: sequence + written, origin });
628
627
  onDispatch?.(id);
629
628
  fannedStatuses.push(result.status);
630
629
  written++;
@@ -646,9 +645,9 @@ class Dispatcher {
646
645
  // the model can finally SEE its own prior output (and reason through its own syntax errors).
647
646
  // Born FOLDED by default (budget-neutral until OPENed); the turn-0 exemplar passes folded:false
648
647
  // (born open — the one worked example the model orients on, thinning the grammar). text/vnd.plurnk.
649
- async writeModelEntry({ verbatim, runId, loopId, turnId, sequence, folded, origin = "model" }) {
648
+ async writeModelEntry({ verbatim, workerId, loopId, turnId, sequence, folded, origin = "model" }) {
650
649
  const row = await this.#db.engine_insert_log_entry.get({
651
- run_id: runId, loop_id: loopId, turn_id: turnId, sequence,
650
+ worker_id: workerId, loop_id: loopId, turn_id: turnId, sequence,
652
651
  origin, source: null, op: "model", suffix: "", signal: null,
653
652
  scheme: null, username: null, password: null, hostname: null, port: null,
654
653
  pathname: null, params: null, fragment: null, lineMarker: null,
@@ -755,41 +754,41 @@ class Dispatcher {
755
754
  // pending = open streams ∪ live children ∪ THIS turn's retrievals (READ/FIND/OPEN, results unseen
756
755
  // until next packet). One rule, one steer, one repair family: nothing pending may be silently
757
756
  // discarded; 499 discards BY STATED INTENT and is never gated.
758
- async #pendingSet(runId, turnId) {
757
+ async #pendingSet(workerId, turnId) {
759
758
  const pending = [];
760
- const openSubs = await this.#db.find_open_subscriptions_for_run.all({ run_id: runId });
759
+ const openSubs = await this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId });
761
760
  const execHandler = this.#schemes.get("exec");
762
- if (openSubs.length > 0 || execHandler?.hasActiveSpawns?.(runId) === true)
763
- pending.push("open streams");
764
- const liveChild = await this.#db.engine_run_has_live_child.get({ run_id: runId });
761
+ if (openSubs.length > 0 || execHandler?.hasActiveSpawns?.(workerId) === true)
762
+ pending.push("surviving streams");
763
+ const liveChild = await this.#db.engine_worker_has_live_child.get({ worker_id: workerId });
765
764
  if (liveChild !== undefined)
766
- pending.push("live worker runs");
765
+ pending.push("surviving workers");
767
766
  const retrievals = await this.#db.engine_turn_retrievals.all({ turn_id: turnId });
768
767
  if (retrievals.length > 0)
769
- pending.push("results of this turn's READ/FIND/OPEN (they arrive NEXT turn)");
768
+ pending.push("this turn's retrieval results (they land in the NEXT packet's Log)");
770
769
  // §send-undelivered-child-term — a worker that concluded DURING this turn's generation is no
771
770
  // longer "live" but its deliverable hasn't reached any packet yet; concluding discards it.
772
- const undelivered = await this.#db.engine_run_has_undelivered_child_term.get({ run_id: runId, turn_id: turnId });
771
+ const undelivered = await this.#db.engine_worker_has_undelivered_child_term.get({ worker_id: workerId, turn_id: turnId });
773
772
  if (undelivered !== undefined)
774
773
  pending.push("worker results that arrived during this turn (they land NEXT turn)");
775
774
  return pending;
776
775
  }
777
776
  // J — a live obligation to WAIT on: a spawned child or an open stream (NOT retrievals, which land
778
777
  // next turn regardless). The wait-side twin of #pendingSet's stream+child legs (§wait-obligation-matrix).
779
- async #hasLiveWork(runId) {
780
- const openSubs = await this.#db.find_open_subscriptions_for_run.all({ run_id: runId });
778
+ async #hasLiveWork(workerId) {
779
+ const openSubs = await this.#db.find_open_subscriptions_for_worker.all({ worker_id: workerId });
781
780
  if (openSubs.length > 0)
782
781
  return true;
783
782
  const execHandler = this.#schemes.get("exec");
784
- if (execHandler?.hasActiveSpawns?.(runId) === true)
783
+ if (execHandler?.hasActiveSpawns?.(workerId) === true)
785
784
  return true;
786
- const liveChild = await this.#db.engine_run_has_live_child.get({ run_id: runId });
785
+ const liveChild = await this.#db.engine_worker_has_live_child.get({ worker_id: workerId });
787
786
  return liveChild !== undefined;
788
787
  }
789
788
  async #handleSendBroadcast(statement, ctx) {
790
789
  if (statement.op !== "SEND")
791
790
  throw new Error("unreachable");
792
- const { runId, loopId, turnId } = ctx;
791
+ const { workerId, loopId, turnId } = ctx;
793
792
  const status = statement.signal;
794
793
  if (status === null)
795
794
  return { status: 400 };
@@ -800,7 +799,7 @@ class Dispatcher {
800
799
  // indefinitely (the butler/worker pattern — owner-ruled ungated; the instrumentation renders
801
800
  // it legibly). Internally the parked state remains loops.status=202: the model-facing SIGNAL
802
801
  // retired, the engine's park state did not.
803
- // §join-blocking-collect (#354) — a READ(run://running-child) this turn armed a join. A BARE
802
+ // §join-blocking-collect (#354) — a READ(worker://running-child) this turn armed a join. A BARE
804
803
  // continue becomes an indefinite PARK: the blocking collect, on the same park machinery <-1>
805
804
  // uses. The armed READ said "I want this result"; parking IS the collect (the model never had
806
805
  // to know the park syntax — the engine holds the join). Any SEND clears the per-turn arm; a
@@ -814,13 +813,13 @@ class Dispatcher {
814
813
  // §wait-obligation-matrix — the WAIT: SEND[202], and the legacy SEND[102]<T> the terminal
815
814
  // redesign spelled the same park with, are one obligation-checked wait (waitpid). A live
816
815
  // obligation (a spawned child or open stream, J) BLOCKS the loop until it concludes and
817
- // reawakens it (§run-lifecycle-child-wake); a wait on nothing (∅) is already satisfied and
816
+ // reawakens it (§worker-lifecycle-child-wake); a wait on nothing (∅) is already satisfied and
818
817
  // resolves like 200, so <-1>+∅ self-resolves rather than hang the agent; a pending own
819
818
  // retrieval (R) just lands next turn, so the wait continues.
820
819
  if (status === 202 || (status === 102 && statement.lineMarker !== null)) {
821
820
  const marks = statement.lineMarker?.marks[0];
822
821
  const seconds = typeof marks === "number" ? marks : -1; // bare 202 / absent T = indefinite, bounded by the join
823
- if (await this.#hasLiveWork(runId)) {
822
+ if (await this.#hasLiveWork(workerId)) {
824
823
  await this.#db.engine_loop_set_status.run({ status: 202, loop_id: loopId, message: raw.length > 0 ? raw : "waiting on live work" });
825
824
  this.#parkDeadlines.set(loopId, seconds);
826
825
  return { status: 202, attrs: { waiting: seconds } };
@@ -833,26 +832,24 @@ class Dispatcher {
833
832
  // NEXT build. The wait is NOT on nothing — the results are on the doorstep. Parking
834
833
  // would hang (the wake edges already fired into an unparked run); R semantics: continue,
835
834
  // the deltas land next turn, the model weighs them and concludes.
836
- const undelivered = await this.#db.engine_run_has_undelivered_child_term.get({ run_id: runId, turn_id: turnId });
835
+ const undelivered = await this.#db.engine_worker_has_undelivered_child_term.get({ worker_id: workerId, turn_id: turnId });
837
836
  if (undelivered !== undefined)
838
837
  return { status: 102 };
839
- // ∅ — a wait on zero obligations is already satisfied; conclude like 200 (incl. <-1>+∅).
840
- // #379 (owner ruling): the ENGINE concluded this loop, not the model mark it as state
841
- // so the COLLECT/delta render the act instead of presenting the park text as a result.
842
- // The model's words (terminal_message) are never rewritten.
843
- await this.#db.engine_loop_set_status.run({ status: 200, loop_id: loopId, message: raw === "" ? null : raw });
844
- await this.#db.engine_loop_mark_terminated_by.run({ terminated_by: "collapse", loop_id: loopId });
845
- return { status: 200 };
838
+ // ∅ — a wait on nothing is a contradiction, the mirror of premature-terminate (owner
839
+ // ruling, #502 run113): concluding it silently laundered the one signal that something
840
+ // was structurally off (the model believed work was pending; nothing was). 409 gives
841
+ // the turn back with the fact — the recovery rail this exact confusion needs.
842
+ return { status: 409, error: "Attempted [202] wait with nothing pending: no surviving streams, no worker runs, no unretrieved results." };
846
843
  }
847
844
  // §send-300-choices — ask the operator and park (the answer returns via loop.inject).
848
845
  // The three-state cascade (owner design): PLURNK_QUESTIONS unset = ALLOWED (not enabled);
849
846
  // =0 = DENIED servicewide (a ceiling the client cannot override); ENABLED requires the
850
- // client to affirmatively pass it per session (settings.questions — the interactive client
851
- // with a human enables its own sessions; headless/bench never asks). Enabled sessions ALSO
847
+ // client to affirmatively pass it per workspace (settings.questions — the interactive client
848
+ // with a human enables its own workspaces; headless/bench never asks). Enabled workspaces ALSO
852
849
  // get the questions.md teaching injected (docEntries) — capability and teaching gate as one.
853
850
  // Disabled → refused with a self-decide steer, never a park into the void.
854
851
  if (status === 300) {
855
- if (!(await SessionSettings.questionsEnabled(this.#db, ctx.sessionId))) {
852
+ if (!(await WorkspaceSettings.questionsEnabled(this.#db, ctx.workspaceId))) {
856
853
  return { status: 409, error: "Operator asks ([300]) are not enabled in this environment — no one is watching to answer. Decide yourself and proceed: SEND[102] to continue, or [200] to conclude." };
857
854
  }
858
855
  // Owner ruling (#346): the question rides the SAME proposal system as file edits and
@@ -880,12 +877,12 @@ class Dispatcher {
880
877
  if (failCount > 0) {
881
878
  return { status: 409, error: `Termination attempted despite ${failCount} failed operation(s) this turn. The errors land in your log next turn — weigh them, then conclude (or SEND[499] to abandon).` };
882
879
  }
883
- const pending = await this.#pendingSet(runId, turnId);
880
+ const pending = await this.#pendingSet(workerId, turnId);
884
881
  if (pending.length > 0) {
885
882
  // Kind-specific steer (owner wording, xpath/topo forensics): retrievals-only is
886
883
  // gemma's read-and-conclude idiom — no lever to pull, the results simply arrive;
887
884
  // KILL/park advice only muddies it. Streams/children keep the remedy steer.
888
- const retrievalsOnly = pending.every((k) => k.startsWith("results of this turn's"));
885
+ const retrievalsOnly = pending.every((k) => k.startsWith("this turn's retrieval results"));
889
886
  if (retrievalsOnly) {
890
887
  // attrs.retrievalOnly — the strike decoupler (owner ruling): atomic-turn-
891
888
  // pretrained models pair fetch-and-answer by habit; the refusal teaches,
@@ -898,7 +895,7 @@ class Dispatcher {
898
895
  // that re-armed this gate four times while she held the correct answer).
899
896
  return { status: 409, error: "Last turn both performed retrieval operations and attempted to terminate. Retrieval operations force an additional turn to receive results for review and reaction. To conclude, only use PLAN and SEND[200] operations.", attrs: { retrievalOnly: true } };
900
897
  }
901
- return { status: 409, error: `Attempted [200] termination with pending work: ${pending.join("; ")}. KILL what you no longer need; SEND[102] (or [102]<seconds>) to receive the rest; then conclude.` };
898
+ return { status: 409, error: `Attempted [200] termination in a turn that performs retrieval or has surviving work: ${pending.join("; ")}. KILL what you no longer need; SEND[102] (or [102]<seconds>) to receive the rest; then conclude.` };
902
899
  }
903
900
  await this.#db.engine_loop_set_status.run({ status: 200, loop_id: loopId, message: raw === "" ? null : raw });
904
901
  return { status: 200 };
@@ -945,7 +942,7 @@ class Dispatcher {
945
942
  return true;
946
943
  return !(statement.op === "SEND" && statement.target === null);
947
944
  }
948
- async #writeLog({ statement, result, runId, loopId, turnId, sequence, origin, }) {
945
+ async #writeLog({ statement, result, workerId, loopId, turnId, sequence, origin, }) {
949
946
  const target = this.#extractTarget(statement.target);
950
947
  const lineMarkerJson = "lineMarker" in statement && statement.lineMarker !== null
951
948
  ? JSON.stringify(statement.lineMarker)
@@ -978,7 +975,7 @@ class Dispatcher {
978
975
  attrsObj.stream = `${runtime}://${coordPathname}`;
979
976
  // Mutate the in-memory result.attrs too: the dispatch path
980
977
  // hands originalResult.attrs to handler.applyResolution after
981
- // proposal accept (see ProposalLifecycle.runApply). Both views —
978
+ // proposal accept (see ProposalLifecycle.workerApply). Both views —
982
979
  // the stored row AND the in-memory proposal — need the same
983
980
  // pathname so applyResolution writes the entry at the same URI.
984
981
  if (result.attrs !== undefined && result.attrs !== null) {
@@ -989,7 +986,7 @@ class Dispatcher {
989
986
  const txJson = JSON.stringify(statement);
990
987
  const rxJson = JSON.stringify(result);
991
988
  const row = await this.#db.engine_insert_log_entry.get({
992
- run_id: runId,
989
+ worker_id: workerId,
993
990
  loop_id: loopId,
994
991
  turn_id: turnId,
995
992
  sequence: sequence,
@@ -1037,10 +1034,10 @@ class Dispatcher {
1037
1034
  // Every registered (plurnk-namespace) scheme uses its authority as a namespace segment — fold
1038
1035
  // it into the canonical pathname so known://x ≡ known:///x ≡ /x and the log keys identically to
1039
1036
  // the entry (/prompt/<run>/<loop>/<N>, /docs/x.md). A foreign web host (http://, unregistered) is NOT a
1040
- // namespace: keep it in hostname. run:// is the one registered EXCEPTION — its authority IS the
1041
- // run selector (§run-scheme), and run://self must stay distinct from run://name, so Run.ts
1037
+ // namespace: keep it in hostname. worker:// is the one registered EXCEPTION — its authority IS the
1038
+ // run selector (§worker-scheme), and worker://self must stay distinct from worker://name, so Run.ts
1042
1039
  // folds the owner into the storage path itself, never here.
1043
- const foldNs = scheme !== null && scheme !== "run" && this.#schemes.has(scheme);
1040
+ const foldNs = scheme !== null && scheme !== "worker" && this.#schemes.has(scheme);
1044
1041
  return {
1045
1042
  scheme, username: path.username, password: path.password,
1046
1043
  hostname: foldNs ? null : path.hostname, port: path.port,