@plurnk/plurnk-service 3.3.0 → 4.0.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 (298) hide show
  1. package/.env.defaults +1 -1
  2. package/INSTALL.md +6 -0
  3. package/README.md +2 -2
  4. package/SPEC.md +380 -159
  5. package/dist/build-info.json +1 -1
  6. package/dist/completions/_plurnk-service +1 -1
  7. package/dist/completions/plurnk-service.bash +1 -1
  8. package/dist/completions/plurnk-service.fish +2 -0
  9. package/dist/content/read-projector.js +1 -1
  10. package/dist/content/read-projector.js.map +1 -1
  11. package/dist/core/AdministrativeLoop.d.ts +1 -1
  12. package/dist/core/AdministrativeLoop.d.ts.map +1 -1
  13. package/dist/core/AdministrativeLoop.js +4 -1
  14. package/dist/core/AdministrativeLoop.js.map +1 -1
  15. package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
  16. package/dist/core/AdmittedTurnExecutor.js +8 -0
  17. package/dist/core/AdmittedTurnExecutor.js.map +1 -1
  18. package/dist/core/BareBatchRunner.js +2 -1
  19. package/dist/core/BareBatchRunner.js.map +1 -1
  20. package/dist/core/ChannelWrite.d.ts +10 -0
  21. package/dist/core/ChannelWrite.d.ts.map +1 -1
  22. package/dist/core/ChannelWrite.js +10 -0
  23. package/dist/core/ChannelWrite.js.map +1 -1
  24. package/dist/core/ChannelWrite.sql +14 -2
  25. package/dist/core/ClientInteractions.d.ts.map +1 -1
  26. package/dist/core/ClientInteractions.js +2 -1
  27. package/dist/core/ClientInteractions.js.map +1 -1
  28. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  29. package/dist/core/CoreSchemeServices.js +5 -0
  30. package/dist/core/CoreSchemeServices.js.map +1 -1
  31. package/dist/core/DataStatementRunner.js +4 -6
  32. package/dist/core/DataStatementRunner.js.map +1 -1
  33. package/dist/core/Dispatcher.d.ts +3 -7
  34. package/dist/core/Dispatcher.d.ts.map +1 -1
  35. package/dist/core/Dispatcher.js +46 -68
  36. package/dist/core/Dispatcher.js.map +1 -1
  37. package/dist/core/Dispatcher.sql +2 -2
  38. package/dist/core/EditMutations.js +1 -0
  39. package/dist/core/EditMutations.js.map +1 -1
  40. package/dist/core/Engine.d.ts +8 -14
  41. package/dist/core/Engine.d.ts.map +1 -1
  42. package/dist/core/Engine.js +22 -24
  43. package/dist/core/Engine.js.map +1 -1
  44. package/dist/core/Engine.sql +6 -6
  45. package/dist/core/ExecutorRegistry.d.ts +0 -5
  46. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  47. package/dist/core/ExecutorRegistry.js +10 -18
  48. package/dist/core/ExecutorRegistry.js.map +1 -1
  49. package/dist/core/FileAddress.js +1 -1
  50. package/dist/core/FileAddress.js.map +1 -1
  51. package/dist/core/FilePublication.js +10 -74
  52. package/dist/core/FilePublication.js.map +1 -1
  53. package/dist/core/FileSnapshot.js +36 -100
  54. package/dist/core/FileSnapshot.js.map +1 -1
  55. package/dist/core/HostPaths.d.ts +1 -0
  56. package/dist/core/HostPaths.d.ts.map +1 -1
  57. package/dist/core/HostPaths.js +11 -1
  58. package/dist/core/HostPaths.js.map +1 -1
  59. package/dist/core/KillHandler.js +3 -1
  60. package/dist/core/KillHandler.js.map +1 -1
  61. package/dist/core/LiveSubscription.js +1 -1
  62. package/dist/core/LiveSubscription.js.map +1 -1
  63. package/dist/core/LogBody.js +1 -1
  64. package/dist/core/LogBody.js.map +1 -1
  65. package/dist/core/LogWriter.js +1 -1
  66. package/dist/core/LogWriter.js.map +1 -1
  67. package/dist/core/LoopDriver.d.ts +3 -3
  68. package/dist/core/LoopDriver.d.ts.map +1 -1
  69. package/dist/core/LoopDriver.js +15 -13
  70. package/dist/core/LoopDriver.js.map +1 -1
  71. package/dist/core/LoopLifecycle.d.ts +5 -0
  72. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  73. package/dist/core/LoopLifecycle.js +14 -1
  74. package/dist/core/LoopLifecycle.js.map +1 -1
  75. package/dist/core/LoopLifecycle.sql +8 -0
  76. package/dist/core/MembershipMaterialization.js +2 -2
  77. package/dist/core/MembershipMaterialization.js.map +1 -1
  78. package/dist/core/MessageResources.d.ts +3 -0
  79. package/dist/core/MessageResources.d.ts.map +1 -1
  80. package/dist/core/MessageResources.js +15 -1
  81. package/dist/core/MessageResources.js.map +1 -1
  82. package/dist/core/OperatorConfig.d.ts +1 -2
  83. package/dist/core/OperatorConfig.d.ts.map +1 -1
  84. package/dist/core/OperatorConfig.js +26 -92
  85. package/dist/core/OperatorConfig.js.map +1 -1
  86. package/dist/core/PacketBuilder.js +6 -6
  87. package/dist/core/PacketBuilder.js.map +1 -1
  88. package/dist/core/PacketBuilder.sql +7 -8
  89. package/dist/core/ProblemLog.d.ts +1 -0
  90. package/dist/core/ProblemLog.d.ts.map +1 -1
  91. package/dist/core/ProblemLog.js +2 -2
  92. package/dist/core/ProblemLog.js.map +1 -1
  93. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  94. package/dist/core/ProposalLifecycle.js +13 -8
  95. package/dist/core/ProposalLifecycle.js.map +1 -1
  96. package/dist/core/ProposalPolicies.d.ts +4 -1
  97. package/dist/core/ProposalPolicies.d.ts.map +1 -1
  98. package/dist/core/ProposalPolicies.js +5 -2
  99. package/dist/core/ProposalPolicies.js.map +1 -1
  100. package/dist/core/ResourceMutations.js +2 -0
  101. package/dist/core/ResourceMutations.js.map +1 -1
  102. package/dist/core/ResourceSelector.js +1 -1
  103. package/dist/core/ResourceSelector.js.map +1 -1
  104. package/dist/core/SchemeRegistry.js +1 -1
  105. package/dist/core/SchemeRegistry.js.map +1 -1
  106. package/dist/core/ServiceHelp.js +1 -1
  107. package/dist/core/ServiceHelp.js.map +1 -1
  108. package/dist/core/ServiceTeardown.js +9 -12
  109. package/dist/core/ServiceTeardown.js.map +1 -1
  110. package/dist/core/StrikeRail.d.ts +2 -1
  111. package/dist/core/StrikeRail.d.ts.map +1 -1
  112. package/dist/core/StrikeRail.js +11 -6
  113. package/dist/core/StrikeRail.js.map +1 -1
  114. package/dist/core/TerminalResult.js +1 -1
  115. package/dist/core/TerminalResult.js.map +1 -1
  116. package/dist/core/TurnDispositionHandler.js +3 -2
  117. package/dist/core/TurnDispositionHandler.js.map +1 -1
  118. package/dist/core/TurnMaterialization.js +6 -4
  119. package/dist/core/TurnMaterialization.js.map +1 -1
  120. package/dist/core/TurnMaterialization.sql +1 -1
  121. package/dist/core/TurnOps.js +1 -0
  122. package/dist/core/TurnOps.js.map +1 -1
  123. package/dist/core/TurnRunner.d.ts +4 -2
  124. package/dist/core/TurnRunner.d.ts.map +1 -1
  125. package/dist/core/TurnRunner.js +63 -51
  126. package/dist/core/TurnRunner.js.map +1 -1
  127. package/dist/core/WorkerControlHandler.d.ts +2 -0
  128. package/dist/core/WorkerControlHandler.d.ts.map +1 -1
  129. package/dist/core/WorkerControlHandler.js +48 -16
  130. package/dist/core/WorkerControlHandler.js.map +1 -1
  131. package/dist/core/WorkerName.d.ts +0 -4
  132. package/dist/core/WorkerName.d.ts.map +1 -1
  133. package/dist/core/WorkerName.js +5 -30
  134. package/dist/core/WorkerName.js.map +1 -1
  135. package/dist/core/WorkerName.sql +4 -20
  136. package/dist/core/WorkerOwners.d.ts +1 -0
  137. package/dist/core/WorkerOwners.d.ts.map +1 -1
  138. package/dist/core/WorkerOwners.js +6 -0
  139. package/dist/core/WorkerOwners.js.map +1 -1
  140. package/dist/core/WorkerOwners.sql +7 -0
  141. package/dist/core/WorkspaceGate.d.ts +3 -1
  142. package/dist/core/WorkspaceGate.d.ts.map +1 -1
  143. package/dist/core/WorkspaceGate.js +59 -10
  144. package/dist/core/WorkspaceGate.js.map +1 -1
  145. package/dist/core/caps/SchemeCtxImpl.d.ts +2 -0
  146. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  147. package/dist/core/caps/SchemeCtxImpl.js +12 -0
  148. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  149. package/dist/core/env-catalog.js +1 -1
  150. package/dist/core/env-catalog.js.map +1 -1
  151. package/dist/core/fork.js +1 -1
  152. package/dist/core/fork.js.map +1 -1
  153. package/dist/core/fork.sql +5 -4
  154. package/dist/core/mutation-types.js +4 -1
  155. package/dist/core/mutation-types.js.map +1 -1
  156. package/dist/core/namespace.js +1 -1
  157. package/dist/core/namespace.js.map +1 -1
  158. package/dist/core/packet-wire.d.ts +1 -1
  159. package/dist/core/packet-wire.d.ts.map +1 -1
  160. package/dist/core/packet-wire.js +19 -14
  161. package/dist/core/packet-wire.js.map +1 -1
  162. package/dist/core/scheme-types.d.ts +4 -0
  163. package/dist/core/scheme-types.d.ts.map +1 -1
  164. package/dist/core/turn-signals.d.ts +4 -0
  165. package/dist/core/turn-signals.d.ts.map +1 -1
  166. package/dist/core/turn-signals.js +1 -0
  167. package/dist/core/turn-signals.js.map +1 -1
  168. package/dist/core/workspace-settings.js +1 -1
  169. package/dist/core/workspace-settings.js.map +1 -1
  170. package/dist/evidence/EvidenceReader.js +5 -5
  171. package/dist/evidence/EvidenceReader.js.map +1 -1
  172. package/dist/man/plurnk-service.1 +24 -3
  173. package/dist/schemes/Env.d.ts +7 -3
  174. package/dist/schemes/Env.d.ts.map +1 -1
  175. package/dist/schemes/Env.js +12 -6
  176. package/dist/schemes/Env.js.map +1 -1
  177. package/dist/schemes/Exec.d.ts.map +1 -1
  178. package/dist/schemes/Exec.js +33 -25
  179. package/dist/schemes/Exec.js.map +1 -1
  180. package/dist/schemes/ExecOutputScheme.js +3 -3
  181. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  182. package/dist/schemes/ExecutionInput.js +1 -1
  183. package/dist/schemes/ExecutionInput.js.map +1 -1
  184. package/dist/schemes/File.js +15 -18
  185. package/dist/schemes/File.js.map +1 -1
  186. package/dist/schemes/Log.d.ts.map +1 -1
  187. package/dist/schemes/Log.js +19 -4
  188. package/dist/schemes/Log.js.map +1 -1
  189. package/dist/schemes/ResourceTreeScheme.js +2 -5
  190. package/dist/schemes/ResourceTreeScheme.js.map +1 -1
  191. package/dist/schemes/TurnSource.js +1 -1
  192. package/dist/schemes/TurnSource.js.map +1 -1
  193. package/dist/schemes/Worker.d.ts.map +1 -1
  194. package/dist/schemes/Worker.js +10 -3
  195. package/dist/schemes/Worker.js.map +1 -1
  196. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  197. package/dist/schemes/_entry-crud.js +2 -4
  198. package/dist/schemes/_entry-crud.js.map +1 -1
  199. package/dist/schemes/_entry-crud.sql +5 -3
  200. package/dist/schemes/_entry-find.js +3 -3
  201. package/dist/schemes/_entry-find.js.map +1 -1
  202. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  203. package/dist/schemes/_entry-manifest.js +3 -3
  204. package/dist/schemes/_entry-manifest.js.map +1 -1
  205. package/dist/schemes/_entry-manifest.sql +2 -0
  206. package/dist/schemes/_entry-ops.js +1 -1
  207. package/dist/schemes/_entry-ops.js.map +1 -1
  208. package/dist/schemes/_entry-send.js +1 -1
  209. package/dist/schemes/_entry-send.js.map +1 -1
  210. package/dist/server/ClientReads.d.ts +1 -0
  211. package/dist/server/ClientReads.d.ts.map +1 -1
  212. package/dist/server/ClientReads.js +7 -2
  213. package/dist/server/ClientReads.js.map +1 -1
  214. package/dist/server/Credentials.d.ts +48 -0
  215. package/dist/server/Credentials.d.ts.map +1 -0
  216. package/dist/server/Credentials.js +203 -0
  217. package/dist/server/Credentials.js.map +1 -0
  218. package/dist/server/Daemon.d.ts +40 -41
  219. package/dist/server/Daemon.d.ts.map +1 -1
  220. package/dist/server/Daemon.js +272 -206
  221. package/dist/server/Daemon.js.map +1 -1
  222. package/dist/server/DrainSupervisor.d.ts +5 -2
  223. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  224. package/dist/server/DrainSupervisor.js +27 -31
  225. package/dist/server/DrainSupervisor.js.map +1 -1
  226. package/dist/server/EnvFunctionality.d.ts.map +1 -1
  227. package/dist/server/EnvFunctionality.js +2 -1
  228. package/dist/server/EnvFunctionality.js.map +1 -1
  229. package/dist/server/Functionality.d.ts +4 -2
  230. package/dist/server/Functionality.d.ts.map +1 -1
  231. package/dist/server/Functionality.js +110 -51
  232. package/dist/server/Functionality.js.map +1 -1
  233. package/dist/server/FunctionalityManager.js +1 -1
  234. package/dist/server/FunctionalityManager.js.map +1 -1
  235. package/dist/server/HttpListener.d.ts +1 -1
  236. package/dist/server/HttpListener.d.ts.map +1 -1
  237. package/dist/server/HttpListener.js.map +1 -1
  238. package/dist/server/MembersFunctionality.d.ts.map +1 -1
  239. package/dist/server/MembersFunctionality.js +4 -2
  240. package/dist/server/MembersFunctionality.js.map +1 -1
  241. package/dist/server/ModuleHost.d.ts +2 -1
  242. package/dist/server/ModuleHost.d.ts.map +1 -1
  243. package/dist/server/PlurnkSkill.js +4 -7
  244. package/dist/server/PlurnkSkill.js.map +1 -1
  245. package/dist/server/Rollback.js +1 -1
  246. package/dist/server/Rollback.js.map +1 -1
  247. package/dist/server/WorkerModelResolver.js +1 -0
  248. package/dist/server/WorkerModelResolver.js.map +1 -1
  249. package/dist/server/WorkerReferences.d.ts +14 -0
  250. package/dist/server/WorkerReferences.d.ts.map +1 -0
  251. package/dist/server/WorkerReferences.js +24 -0
  252. package/dist/server/WorkerReferences.js.map +1 -0
  253. package/dist/server/WorkerReferences.sql +31 -0
  254. package/dist/server/WorkspaceCapabilities.d.ts +2 -0
  255. package/dist/server/WorkspaceCapabilities.d.ts.map +1 -1
  256. package/dist/server/WorkspaceCapabilities.js +35 -18
  257. package/dist/server/WorkspaceCapabilities.js.map +1 -1
  258. package/dist/server/WorkspacePlugins.js +1 -0
  259. package/dist/server/WorkspacePlugins.js.map +1 -1
  260. package/dist/server/WorkspaceResidency.d.ts +3 -1
  261. package/dist/server/WorkspaceResidency.d.ts.map +1 -1
  262. package/dist/server/WorkspaceResidency.js +12 -5
  263. package/dist/server/WorkspaceResidency.js.map +1 -1
  264. package/dist/server/client-input.d.ts +2 -0
  265. package/dist/server/client-input.d.ts.map +1 -1
  266. package/dist/server/client-input.js +28 -0
  267. package/dist/server/client-input.js.map +1 -1
  268. package/dist/server/drain.sql +37 -40
  269. package/dist/server/envelope.d.ts +5 -9
  270. package/dist/server/envelope.d.ts.map +1 -1
  271. package/dist/server/envelope.js +21 -84
  272. package/dist/server/envelope.js.map +1 -1
  273. package/dist/server/envelope.sql +17 -17
  274. package/dist/server/lifecycle-recovery.sql +5 -15
  275. package/dist/server/logEntry.d.ts.map +1 -1
  276. package/dist/server/logEntry.js +6 -0
  277. package/dist/server/logEntry.js.map +1 -1
  278. package/dist/server/logEntry.sql +94 -6
  279. package/dist/server/module-discovery.d.ts +1 -1
  280. package/dist/server/module-discovery.d.ts.map +1 -1
  281. package/dist/server/module-discovery.js +1 -0
  282. package/dist/server/module-discovery.js.map +1 -1
  283. package/dist/server/seam-loop.sql +15 -7
  284. package/dist/server/seam-proposal-list.sql +10 -4
  285. package/dist/service.d.ts.map +1 -1
  286. package/dist/service.js +7 -1
  287. package/dist/service.js.map +1 -1
  288. package/docs/members.md +6 -4
  289. package/migrations/002_workers.sql +2 -2
  290. package/migrations/006_log.sql +1 -1
  291. package/migrations/018_worker_owners.sql +1 -1
  292. package/migrations/029_default_worker.sql +9 -0
  293. package/migrations/030_client_workers.sql +105 -0
  294. package/migrations/031_message_openers.sql +83 -0
  295. package/migrations/032_worker_references.sql +29 -0
  296. package/package.json +23 -22
  297. package/plurnk.nginx.conf +71 -0
  298. package/plurnk.service +2 -0
package/SPEC.md CHANGED
@@ -40,11 +40,11 @@ Canonical meanings. When a doc, comment, test name, or commit message uses one o
40
40
 
41
41
  ```mermaid
42
42
  flowchart LR
43
- W["Workspace<br/>shared world"] --> K["Worker<br/>actor and history"]
43
+ W["Workspace<br/>shared resources and configuration"] --> K["Worker<br/>actor and history"]
44
44
  K --> L["Loop<br/>queued-to-terminal work"]
45
45
  L --> T["Turn<br/>one admitted scheduling unit"]
46
46
  T --> O["Op<br/>one statement / action row"]
47
- H["AG-UI thread<br/>conversation identity"] -. resolves to .-> K
47
+ H["AG-UI thread<br/>names a worker"] -. resolves to .-> K
48
48
  R["AG-UI Run<br/>client stream envelope"] -. binds to .-> L
49
49
  R -. or executes .-> A["Management action<br/>no core loop required"]
50
50
  ```
@@ -52,7 +52,7 @@ flowchart LR
52
52
  | Term | Layer | Meaning |
53
53
  |-------------------|-----------------------|---------|
54
54
  | **agent** | PLURNK | The plurnk runtime. Acts in-workspace as the `_plurnk` worker ({§actor-boundary} self-hosting), never a privileged singleton owning its own entries ({§entry-owner}, {§machine-processes}). |
55
- | **workspace** | Core | Durable user-named shared world. Persists across workers and process restarts. Identity: `workspaces.id` + unique `workspaces.name`. |
55
+ | **workspace** | Core | Durable shared resources and configuration, with an optional project folder. Identity: `workspaces.id`; mutable label: unique `workspaces.name`. Persists across workers and process restarts. |
56
56
  | **worker** | Core | Durable actor and history over one workspace. Owns its loops and log rows, may carry a `parent_worker_id`, and has one process-local cancellation scope while active. |
57
57
  | **loop** | Core | Queued-to-terminal unit of model or client work within a worker. Status ∈ {100 pending · 102 running · 200 done · 202 waiting (blocked on a live obligation, {§send}) · 413 input-capacity failure · 429 model-turn ceiling · 499 cancelled · 500 failed · 504 execution timeout ({§operator-config-loop-timeout}) · 508 runaway}. Many loops may belong to one worker. |
58
58
  | **turn** | Core | One durable, producer-neutral batch of ordered operations. A turn may be authored by a model, client, or `_plurnk`; only a model turn assembles a packet and owns an emission call. Many turns may belong to one loop. Identity: `(loop_id, sequence)`. |
@@ -62,7 +62,7 @@ flowchart LR
62
62
  | **action** | Core | One executed op. Execution normally produces a `log_entries` row at `log:///<L>/<T>/<S>/<op>`; an engine rail may instead record an `op='error'` row ({§operation-results}). A source artifact carries no fabricated operation. |
63
63
  | **dispatch** | Core | Routing a statement to its scheme's op handler. |
64
64
  | **AG-UI Run** | AG-UI protocol | A client request/stream envelope identified by the client's `runId`. A message or resume AG-UI Run binds to one core loop; a management-action AG-UI Run may complete without creating a core loop. |
65
- | **AG-UI thread** | AG-UI protocol | Conversation identity. Within an explicitly selected workspace, `threadId` resolves to one conversation worker. |
65
+ | **AG-UI thread** | AG-UI protocol | Message history identity. Within an explicitly selected workspace, `threadId` resolves to one model worker. |
66
66
 
67
67
  ### Storage terms
68
68
 
@@ -202,7 +202,8 @@ early ownership introduces neither traffic nor a close/rebind race.
202
202
  §http-host **The daemon opens exactly one transport.** Core binds the HTTP
203
203
  listener on `PLURNK_HOST:PLURNK_PORT` and offers it to every exterior adapter as
204
204
  `registerHttpRoute(prefix, handler)` and `httpAddress()` on the application port
205
- ({§application-port}). A prefix is an absolute pathname. Each request goes to the
205
+ ({§application-port}). The listener is bound before any module sets up, so the setup seam
206
+ offers `httpAddress()` too ({§module-host-capabilities}). A prefix is an absolute pathname. Each request goes to the
206
207
  longest mounted prefix; a prefix claims itself and the subtree beneath it, never
207
208
  a longer sibling name; `/` is the root and receives whatever nothing more
208
209
  specific claimed when a module mounts it. An admitted listener returns an HTTP
@@ -215,6 +216,67 @@ none opens a socket of its own. The standards
215
216
  address by URL, never by port (#641): AG-UI mounts only `/agui`, A2A the
216
217
  well-known card and its endpoint path, on the same address.
217
218
 
219
+ §http-request-origin **What the daemon advertises follows the address a caller used.** A request's
220
+ origin is the address its `Host` names, over http, or the listener's own for a request that names
221
+ none (`requestOrigin`). Every address the daemon advertises to a caller, the authority's issuer
222
+ ({§agui-oauth-endpoints}) and the A2A card's endpoint, is its configured public origin or the origin
223
+ of the request that asked, so a daemon bound to every interface answers each caller at the address
224
+ that caller used; a module that asks with no request in hand, as one reading the issuer at its
225
+ start does, is answered with the configured origin or the listener's own. A configured origin is for
226
+ a front the daemon cannot see, such as a TLS proxy. No request is refused for the name it uses.
227
+
228
+ §credential-authority **The daemon is one credential authority.** Every credential presented
229
+ to its listener, whatever protocol is mounted there, was issued by the daemon's `credentials`
230
+ authority on the application port ({§application-port}): an opaque random token held in memory
231
+ with its kind (`access`, presented as a bearer; `refresh`, exchanged and never presented), its
232
+ subject, its client, its scopes, its family and its expiry. A restart revokes every credential.
233
+ Revocation is by family, so an access token and the refresh token that renews it fall together;
234
+ a refresh token is consumed once, and a second presentation revokes its family and says so. The
235
+ authority has no HTTP face and no policy of its own: the issuing module publishes the one face
236
+ (`publish({ issuer })`, at setup) that names where a request's caller obtains credentials
237
+ ({§http-request-origin}) and answers from its publication, so any module may read the issuer from its own `start` on ({§module-phases}), an
238
+ exterior adapter
239
+ verifies through it ({§agui-http-authorization}), and the client protocol issues through it
240
+ ({§agui-oauth}). The posture is the shared `PLURNK_AUTH` ({§operator-config-shared-keys}) and is
241
+ never inferred from what else is set. Under `1` the authority holds the operator's client secret,
242
+ `PLURNK_SECRET` when set, else one minted for this boot and written to the file `secret` beside the
243
+ database, 0600 and complete or absent, before any mount is admitted, where a tool running as the
244
+ operator reads it; a configured secret and a daemon that does not authenticate leave no such file. A
245
+ listening daemon under `1` refuses to start until a module publishes the face. Under `0` no
246
+ credential exists, no face is published and every mounted resource admits anonymously. Credentials
247
+ the daemon presents to others follow the remote party's declared scheme and are never minted here.
248
+
249
+ §credential-module-client **A module of this daemon is a confidential client by registration.** At
250
+ setup a module registers its own client on the authority, `registerClient({ id, redirectPaths, scopes, stylesheetPath })`:
251
+ an id of letters, digits, `.`, `_` or `-` other than `operator`, the absolute pathnames under its own
252
+ mounts it may be answered at, each as a URL carries it, with no query and no fragment, the scopes it
253
+ may ask for and, optionally, the pathname in the same form of a stylesheet under its own mounts,
254
+ which the issuing face links on the documents it serves that client's requests
255
+ ({§agui-oauth-documents}). A module states no origin: it serves only on the daemon's one listener
256
+ ({§http-host}), so its address is its path on the issuer, and registration needs nothing from the
257
+ listener, the issuer or another module. The issuing face answers the client at exactly the issuer
258
+ followed by a registered path ({§agui-oauth-endpoints}), and the module forms the same address from
259
+ `credentials.face().issuer()`, from its `start` on ({§credential-authority}). The authority mints the client's
260
+ secret for this boot, holds it in memory, answers it to the registrant and to nobody else, and
261
+ checks it in constant time (`authenticateClient`); `client(id)` tells any module the registration
262
+ without the secret. A second registration of one id and a malformed registration are faults and
263
+ register nothing. Nothing is configured, printed or persisted, so installing a module adds no
264
+ operator step. The issuing face authenticates such a client as it does any confidential one
265
+ ({§agui-oauth-clients}). A module's own browser session is a credential of this authority too: a
266
+ `session` credential it issues in the family of the authorization grant it stands on, which is the
267
+ family of the browser's sign-in that authorized it ({§agui-oauth-session}), so revoking that family
268
+ from either side ends the module's session, its tokens and the browser's standing at the issuing
269
+ face together.
270
+
271
+ §credential-sessions **A browser sign-in is a family, and a subject's sign-ins are listed without a
272
+ token.** `sessions(subject)` answers every family of that subject that holds a live `session`
273
+ credential, oldest first: the family, which is the sign-in's handle and is never admitted as a
274
+ credential; the subject; when a credential was first issued into the family; when the last of its
275
+ session credentials expires; and the clients holding credentials in it. A grant with no session
276
+ credential, such as a client's `client_credentials` token or a consented client's code grant, is no
277
+ sign-in and is not listed. Revoking a listed family ({§credential-authority}) is signing that browser
278
+ out. Nothing records last activity or a device, and a restart ends every sign-in.
279
+
218
280
  §startup-admission-order After listener ownership, database admission completes
219
281
  before capability initialization can perform external work. Provider construction
220
282
  and endpoint verification occur on worker selection or first use, never merely
@@ -262,7 +324,7 @@ not access to workspace resources ({§actor-boundary-no-mutex}).
262
324
  | Root creation | Explicit owner, otherwise the runtime `_plurnk`. |
263
325
  | WORK, FORK, or a fresh child | Inherit the parent's owner. |
264
326
  | SEND, schedule occurrence, or other incoming message | Preserve the recipient's owner. |
265
- | Explicit client control attachment | Claim a runtime-owned conversation and its runtime-owned descendants; never claim the runtime actor or replace another owner. |
327
+ | Explicit client control attachment | Claim a runtime-owned model worker and its runtime-owned descendants; never claim the runtime actor or replace another owner. |
266
328
  | Observation or disconnect | No change. |
267
329
 
268
330
  An owner declares its capability set: the client tool names it implements, and whether it
@@ -313,12 +375,12 @@ filter over workspace resources.
313
375
  **attribution** — the delta's provenance ({§env-delta}) — and is never read to
314
376
  filter a row.
315
377
 
316
- §actor-boundary-attached-functionality **Attachment selects a conversation,
378
+ §actor-boundary-attached-functionality **Attachment selects a worker,
317
379
  not an environment owner.** Client management commands mutate workspace
318
380
  Functionality ({§module-workspace-capabilities}); runtimes, runtime
319
- schemes, and tools resolve by `workspaceId`. Operations journal in the
320
- submitting actor's `workerId` ({§connection-lifecycle}), which must belong to
321
- the workspace. Runtime and policy resolution require no second worker identity.
381
+ schemes, and tools resolve by `workspaceId`. A model's operations journal in its
382
+ worker, and a person's client operations in the workspace's runtime worker
383
+ ({§connection-lifecycle}). Runtime and policy resolution require no second worker identity.
322
384
  Attachment remains many-to-one and unrelated to parentage. Explicit control
323
385
  attachment may claim approval ownership under {§worker-ownership}.
324
386
 
@@ -422,7 +484,7 @@ direct-entry-plus-directory count; `-1` enables the ordinary markerless page;
422
484
  unset / `0` disables previews. `log://` is absent because the current worker's
423
485
  log already renders in present mode.
424
486
 
425
- §worker-initialization-entry **Model-worker initialization is a real `_plurnk` operation turn.** A model worker's first loop begins with one packetless `{ producer="_plurnk", kind="initialization" }` turn submitted through {§turn-ops-admission-path}. Its program — the READ/FIND surveys, led by one NOTE when the surveys run — is recorded as the turn's `ops://` source and executed through {§op-execution-order} exactly as a model's program is; it authors no reasoning and reads none back. The NOTE keeps the example turn within the card's NOTE-per-turn teaching: it carries the aside of the card's first NOTE example, `<!-- summary -->`, and reads, exactly, `Surveyed tooling, documentation, extended context, and project root.` No emission row is fabricated ({§emission-row}): the first request sees the NOTE and the surveys' ordinary results, never an assistant-content copy of the program. The initial message arrives as an inbound SEND in the first model turn ({§message-arrival}); initialization never READs it, and neither initialization nor later turns manufacture a task inventory. Every orienting row is structurally classified `_plurnk` and `init`. The namespace surveys and their asides follow {§actor-boundary-catalog-preview}.
487
+ §worker-initialization-entry **Model-worker initialization is a real `_plurnk` operation turn.** A model worker's first loop begins with one packetless `{ producer="_plurnk", kind="initialization" }` turn submitted through {§turn-ops-admission-path}. Its program — the READ/FIND surveys, opened by one NOTE and closed by another when the surveys run — is recorded as the turn's `ops://` source and executed through {§op-execution-order} exactly as a model's program is; it authors no reasoning and reads none back. The two NOTEs show the card's NOTE practice in the example turn: the opening one carries the aside of the card's first NOTE example, `<!-- plan -->`, and is the worker's plan in numbered steps, each with its purpose: survey what exists, read the Open Message and restate what it asks for and what will show it is done, then do the work recording each finding, decision and next action in a NOTE; the closing one carries the aside of the card's last NOTE example, `<!-- next -->`, and reads `Read and resolve the Open Message.` No emission row is fabricated ({§emission-row}): the first request sees the NOTEs and the surveys' ordinary results, never an assistant-content copy of the program. The initial message arrives as an inbound SEND in the first model turn ({§message-arrival}); initialization never READs it, and neither initialization nor later turns manufacture a task inventory. Every orienting row is structurally classified `_plurnk` and `init`. The namespace surveys and their asides follow {§actor-boundary-catalog-preview}.
426
488
 
427
489
  Incoming messages publish once as inbound SEND rows in the first model turn
428
490
  ({§message-arrival}); initialization neither READs nor archives them. The turn
@@ -481,8 +543,8 @@ for a loop, `(loop, sequence)` for a turn, `(turn, sequence)` for a log row —
481
543
  outside the database, every copied row passes the same table triggers the original did, and a fork
482
544
  either lands whole or not at all. Nothing else runs: `Fork.fork` is the claim.
483
545
 
484
- §machine-processes-worker-is-its-log **A worker's conversational memory of
485
- the shared world is its log, with no hidden per-worker snapshot beside it.**
546
+ §machine-processes-worker-is-its-log **A worker's memory of
547
+ the shared workspace is its log, with no hidden per-worker snapshot beside it.**
486
548
  A scoped KILL suppresses canonical body intervals on that worker's rows ({§log-kill-scope});
487
549
  lineage activity and explicit commons broadcasts arrive as attributed log
488
550
  entries ({§env-delta}). Named scratch and evidence follow {§machine-processes-entry-inheritance}. They are not an invisible mirror of shared state. Workspace
@@ -497,9 +559,9 @@ the parent after that boundary is sibling activity, not fork history. Global
497
559
  commons broadcasts remain live after the fork because the branch is then an
498
560
  existing workspace worker in its own right.
499
561
 
500
- §machine-processes-model-worker-readable **Packet membership is per-worker, not an access policy.** A packet contains its worker's log plus explicitly delivered activity ({§actor-boundary}). `readLog({ workspaceId, workerId })` may inspect any worker belonging to that workspace, and `listWorkers` enumerates them. A client-interface module chooses its conversation binding. Inspection does not inject another log into a model's packet.
562
+ §machine-processes-model-worker-readable **Packet membership is per-worker, not an access policy.** A packet contains its worker's log plus explicitly delivered activity ({§actor-boundary}). `readLog({ workspaceId, workerId })` may inspect any worker belonging to that workspace, and `listWorkers` enumerates them. A client-interface module chooses its worker binding. Inspection does not inject another log into a model's packet.
501
563
 
502
- §machine-processes-worker-origin **A worker carries its actor.** Each worker records its `origin` — `model` (a conversation), `client` (a client-interface actor), or `_plurnk` (the runtime's self-hosting worker) — set once at creation and inherited by WORK/FORK from the parent. A turn's producer is independent: a client-produced program in a model worker still delegates model workers. `listWorkers` returns actor class without interpreting names; a retained worker's name is immutable ({§machine-processes-worker-is-its-log}).
564
+ §machine-processes-worker-origin **A worker carries its actor.** Each worker records its `origin`, set once at creation: `model` (a model's identity) or `_plurnk` (the workspace's runtime worker, where the daemon's own work and a person's client operations journal, {§connection-lifecycle}). Every worker WORK or FORK creates is a model worker: a model's is its child, and a person's operation starts a root worker or branches the worker it acts for ({§actor-boundary-client-operations}). `listWorkers` returns actor class without interpreting names; a retained worker's name is immutable ({§machine-processes-worker-is-its-log}).
503
565
 
504
566
  §worker-provider-identity **A worker owns a durable provider identity distinct
505
567
  from its database id.** Creation mints a globally unique, opaque 128-bit value;
@@ -508,7 +570,7 @@ emission ({§provider-cache-identity}). Database ids remain the internal relatio
508
570
  client coordinate. BARE calls use isolated per-call provider identities rather
509
571
  than either worker value.
510
572
 
511
- §machine-processes-fork-shares-the-world **A fork copies history and named
573
+ §machine-processes-fork-shares-workspace **A fork copies history and named
512
574
  scratch while sharing the workspace.** It is a new worker in the
513
575
  same workspace (`workers.parent_worker_id`, {§lifecycle-terms}); project files,
514
576
  shared entries, and membership remain live and uncopied.
@@ -523,9 +585,10 @@ or membership overlay requires a new workspace.
523
585
  §worker-authority-carving **Authority is a literal namespace, not a principal.** `worker:///notes.md` is shared scratch; `worker://alice/draft.md` is named scratch. Both belong directly to the workspace ({§entry-owner}). Entry addresses require no namesake Worker row and survive its deletion; only pathless worker controls resolve an actor. The caller never alters an address, and `~` has no alias semantics.
524
586
 
525
587
  §worker-name-minting **URI ingestion is permissive; worker minting is not.**
526
- Every model/client worker-creation door applies the contracts-owned
588
+ Every model worker-creation door applies the contracts-owned
527
589
  `WORKER_NAME` predicate ({§worker-name}) through one core admission path. Generic URL parsing
528
- continues to decompose other authorities without treating them as mintable.
590
+ continues to decompose other authorities without treating them as mintable, and an exact read by
591
+ name ({§methods-worker-read}) is no creation door: it resolves `_plurnk` like any listed name.
529
592
 
530
593
  | Candidate | Minting result |
531
594
  | ---------------------------------------------- | --------------------------------------------------------------------- |
@@ -566,7 +629,7 @@ Every admitted authority is a literal `workers.name`; self-addressing uses the c
566
629
  | `READ` | existing literal name | Collect the named worker's deliverable. |
567
630
  | `KILL` | existing literal name | Terminate the named worker or caller. |
568
631
 
569
- - §worker-scheme-spawn **Spawn** — ```` ```WORK (worker://<name>)? ```` with a task body creates a new child worker (empty log) and starts it with that task on its first loop. WORK/FORK are the worker-creation verbs: EDIT is file/entry only, so EDIT on the bare worker entity is a **400** steering to WORK/FORK — the entity is not an entry. Names remain unique within a workspace for the lifetime of retained Worker rows, including after termination. A WORK whose name a worker already holds is the voice door ({§worker-scheme-irc}): its body is delivered to that worker as a message, nothing is created, the receipt names the worker and `delivered: message`, and an `env` option is refused by name since a message takes none. A FORK whose name a worker holds is `409 worker-name-conflict` whose recovery names the working forms, WORK or `SEND (worker://<name>)` to message the live worker, or a name no worker holds to branch; concurrent claims cannot redirect a published address or expose a raw uniqueness failure.
632
+ - §worker-scheme-spawn **Spawn** — ```` ```WORK (worker://<name>)? ```` with a task body creates a new child worker (empty log) and starts it with that task on its first loop; a person's operation starts a root worker that person owns instead ({§actor-boundary-client-operations}). WORK/FORK are the worker-creation verbs: EDIT is file/entry only, so EDIT on the bare worker entity is a **400** steering to WORK/FORK — the entity is not an entry. Names remain unique within a workspace for the lifetime of retained Worker rows, including after termination. A WORK whose name a worker already holds is the voice door ({§worker-scheme-irc}): its body is delivered to that worker as a message, nothing is created, the receipt names the worker and `delivered: message`, and an `env` option is refused by name since a message takes none. A FORK whose name a worker holds is `409 worker-name-conflict` whose recovery names the working forms, WORK or `SEND (worker://<name>)` to message the live worker, or a name no worker holds to branch; concurrent claims cannot redirect a published address or expose a raw uniqueness failure.
570
633
  - §worker-spawn-prompt-resource **The spawn slot is overloaded by scheme.** A `worker://` path is
571
634
  the child's address and keeps the address rules ({§worker-control-addressing}). A path of any
572
635
  other scheme is the child's prompt resource: it is read whole (`<1,-1>`) under the caller's read capabilities, composed with
@@ -580,7 +643,8 @@ Every admitted authority is a literal `workers.name`; self-addressing uses the c
580
643
  - §worker-scheme-fork **Fork** — ```` ```FORK (worker://<name>)? ```` with a task body branches the
581
644
  current worker into a **named** child: its log is deep-copied
582
645
  ({§machine-processes-fork-copies-the-log}), which continues with `task`; the
583
- world is shared, never copied ({§machine-processes-fork-shares-the-world}).
646
+ workspace is shared, never copied ({§machine-processes-fork-shares-workspace}). A
647
+ person's operation branches the worker it acts for ({§actor-boundary-client-operations}).
584
648
  WORK and FORK are distinct verbs — WORK spawns a fresh worker, FORK branches
585
649
  the log. Both report the resulting `worker://name` address in receipt
586
650
  metadata `worker`, whether named explicitly or allocated automatically.
@@ -618,9 +682,8 @@ Every admitted authority is a literal `workers.name`; self-addressing uses the c
618
682
  A failure reports its problem even when the loop answered earlier: the failure is the news.
619
683
  READs and completion observations use the same resolution, representation and projector.
620
684
  - §child-orientation **Child orientation.** Beyond the conclusion delta, every
621
- turn the packet's status clump surfaces the live things this worker currently
622
- holds under the teaching's own word for handing work out: `## Delegation` is
623
- one JSON object, `{"workers": [...], "streams": [...], "messages": [...]}`, its
685
+ turn the packet's status clump surfaces the work this worker currently holds
686
+ open, beside the messages it owes: `## Open Work` is one JSON object, `{"workers": [...], "streams": [...], "messages": [...]}`, its
624
687
  unconcluded child workers, its open streams and the messages it sent that await
625
688
  completion ({§worker-obligations}) as `{status, path}` pointers (the same shape as
626
689
  the errors section); a message pointer carries the recipient loop's status. Body-suppressed child activity is durable
@@ -643,7 +706,7 @@ Every admitted authority is a literal `workers.name`; self-addressing uses the c
643
706
  Workspace schedules are not held work: an occurrence arrives as a message
644
707
  ({§schedule-delivery}).
645
708
  - §packet-empty-sections **Emptiness is stated where the model decides on it.**
646
- `## Delegation` renders every turn, each of its two lists `[]` when empty: the
709
+ `## Open Work` renders every turn, each of its lists `[]` when empty: the
647
710
  model decides whether to wait or complete on exactly these facts, so their
648
711
  emptiness is stated rather than inferred from a missing heading. This is the
649
712
  deliberate exception to the wire rule that empty content is absent; errors,
@@ -867,7 +930,7 @@ pass.
867
930
 
868
931
  §membership-git-flags **Permission flags.** Service-wide Git admission comes from {§operator-config-git-ceiling}. `PLURNK_SERVICE_GIT_AUTO=1` includes the repository containing `project_root`; `=0` disables automatic Git membership, leaving member definitions as the only membership source. `ALLOWED` gates `AUTO`.
869
932
 
870
- **Rationale.** Workspace is the right scope unit and the containing Git repository is its ordinary development boundary. Membership curation is tiered: Git bounds it by tracking, the client supersedes by overlay, and the model curates its render by READ/KILL. Supporting several independent repositories as one world would require Plurnk-owned topology, synchronization, and model teaching that Git already solves cleanly by treating them as separate workspaces.
933
+ **Rationale.** Workspace is the right scope unit and the containing Git repository is its ordinary development boundary. Membership curation is tiered: Git bounds it by tracking, the client supersedes by overlay, and the model curates its render by READ/KILL. Supporting several independent repositories as one workspace would require Plurnk-owned topology, synchronization, and model teaching that Git already solves cleanly by treating them as separate workspaces.
871
934
 
872
935
  **Schema.** The version-1 baseline stores the normalized {§inference-ledger},
873
936
  its model-response evidence, emission admission, and cardinal
@@ -1144,7 +1207,7 @@ boundary.
1144
1207
  - §worker-lifecycle-idle-is-concluded **Idle is not completion.** A loop concludes only under {§loop-completion}; an unanswered message remains an obligation even when nothing is running. A concluded worker retains durable history; a later addressed arrival starts a new loop.
1145
1208
  - §worker-lifecycle-no-lost-loop **A loop is never stranded by a drain's exit.** A drain relinquishes its registry slot only after a lock-held re-claim confirms the queue is empty; a loop enqueued during that teardown is either re-claimed by the exiting drain or claimed by a fresh drain that a later inject starts. The relinquish and the start are serialized, so neither the lost-loop hang nor a transient double-drain can occur.
1146
1209
  - §worker-lifecycle-durable-disposition **Durable disposition wins cancellation races.** At a turn boundary, the engine reads the loop's durable status before interpreting a process-local abort. A committed `202` park survives a later daemon-shutdown signal; only a loop still durably running at `102` can be terminalized by that cancellation. Wake selection rechecks shutdown and worker cancellation before requeuing each parked loop.
1147
- - §worker-lifecycle-restart-recovery **Restart is owner-loss reconciliation, not replay.** Before opening client transports, the service holds an exclusive database-adjacent daemon lock; a second live owner fails before touching SQLite, while a dead-PID crash claim is replaced atomically without a timeout lease. Boot preserves accepted `100` loops and restores their drains. A `102` loop belonged to a vanished drain/provider call, so it settles `500` with the interruption on its durable row—never replayed across an unknown effect boundary. Every pending physical provider request first settles as an error with absent usage and explicitly unknown cost; then its logical model call closes. Recovery never fabricates zero evidence. Every durable proposed operation likewise lost its process-local resolution waiter and settles as a visible `500 owner_vanished` occurrence rather than an unresolvable interrupt ({§proposal-list}). A pending client interaction also lost its exact awaiting operation, so boot removes the orphan instead of replaying work or inventing a response ({§client-interactions}). Every durable-open subscription belonged to a vanished callable: active channels become errored and its row closes `500`. A `202` continuation requeues on an unseen completion or when no live obligation remains. Otherwise it stays parked on surviving children; the drain restores inherited stream observation through the same guarded scheduler ({§worker-wait-timing}). Child terminalization wakes its parked parent on every outcome, including provider exceptions, cancellation, and restart interruption, recursively through the durable parent edges. These operations are idempotent, so an interrupted recovery safely repeats.
1210
+ - §worker-lifecycle-restart-recovery **Restart is owner-loss reconciliation, not replay.** Before opening client transports, the service holds an exclusive database-adjacent daemon lock; a second live owner fails before touching SQLite, while a dead-PID crash claim is replaced atomically without a timeout lease. Database-only owner-loss settlement commits atomically before drains or process-local observation resume; a failed settlement leaves no partial transitions. Boot preserves accepted `100` loops and restores their drains. A `102` loop belonged to a vanished drain/provider call, so it settles `500` with the interruption on its durable row—never replayed across an unknown effect boundary. Every pending physical provider request first settles as an error with absent usage and explicitly unknown cost; then its logical model call closes. Recovery never fabricates zero evidence. Every durable proposed operation likewise lost its process-local resolution waiter and settles as a visible `500 owner_vanished` occurrence rather than an unresolvable interrupt ({§proposal-list}). A pending client interaction also lost its exact awaiting operation, so boot removes the orphan instead of replaying work or inventing a response ({§client-interactions}). Every durable-open subscription belonged to a vanished callable: active channels become errored and its row closes `500`. A `202` continuation requeues on an unseen completion or when no live obligation remains. Otherwise it stays parked on surviving children or unanswered inter-worker messages; the drain restores inherited stream observation through the same guarded scheduler ({§worker-wait-timing}). Child terminalization wakes its parked parent on every outcome, including provider exceptions, cancellation, and restart interruption, recursively through the durable parent edges. These operations are idempotent, so an interrupted recovery safely repeats.
1148
1211
 
1149
1212
  ---
1150
1213
 
@@ -1241,7 +1304,9 @@ alone are not a cycle: changing inputs or observations distinguish activity.
1241
1304
  A turn that executed nothing has no activity to identify, so an empty turn's
1242
1305
  identity is its **text** ({§empty-turn}) — the same principle, applied to the only
1243
1306
  output it produced. Identifying it by its absent program instead makes every empty
1244
- turn identical, and changing words then read as a repeating one.
1307
+ turn identical, and changing words then read as a repeating one. A turn whose final emission the
1308
+ output allowance cut has no identity: the engine's ceiling, not the model, ended its output, so it
1309
+ is no repetition evidence and closes the window ({§loop-rail-continuity}).
1245
1310
  This is an exact-repetition backstop, not a semantic judgment of task progress;
1246
1311
  new asynchronous invocation identities do not prove repetition of their eventual
1247
1312
  effects. Ordinary contract strikes and operator budgets remain independent.
@@ -1312,12 +1377,13 @@ independent turn ceiling terminates at **429** ({§loop-terminals}). The streak
1312
1377
  and cycle verdict are absent from model packets; only the concrete occurrences
1313
1378
  in the table are shown. The streak never leaves the daemon.
1314
1379
 
1315
- A crossing terminal names the source that struck the crossing turn — `repetition`,
1316
- `no_operation`, then `operation` — in its detail, in that order when a turn matches more
1317
- than one. The three are not interchangeable: a turn that authored no operation did not *fail*
1380
+ A crossing terminal names the source that struck the crossing turn — `output_allowance`,
1381
+ `repetition`, `no_operation`, then `operation` — in its detail, in that order when a turn matches
1382
+ more than one. The three are not interchangeable: a turn that authored no operation did not *fail*
1318
1383
  one, and reporting it as a failed turn misreads a model answering without the fence as a model
1319
1384
  whose operations broke. A cycle of empty turns names repeated responses without operations,
1320
- not repeated operations or results. This is the crossing turn's source, not the streak's composition; the
1385
+ not repeated operations or results. A turn whose final emission the output allowance cut names
1386
+ the allowance and its grant ({§output-allowance-notice}), whatever else it struck on. This is the crossing turn's source, not the streak's composition; the
1321
1387
  rail rules on the crossing and does not retain the kinds behind it. What the crossing turn
1322
1388
  actually said is cited, not discarded ({§terminal-evidence}). Naming the source is not the
1323
1389
  private accounting {§rail-accounting-private} withholds: the streak, the cycle verdict and
@@ -1333,6 +1399,7 @@ restart; curation of log evidence cannot alter them.
1333
1399
  | Assessed turn with a violation | Increment once. | Include its exact activity. |
1334
1400
  | Clean assessed turn | Reset to zero. | Include its exact activity. |
1335
1401
  | Actual park, including an immediate wake/reclaim in the same drain | Preserve the assessed streak. | Close the window; the next turn starts a new one. |
1402
+ | Assessed turn whose final emission the output allowance cut | As any assessed turn. | Exclude it and close the window; the next turn starts a new one. |
1336
1403
  | Recoverable provider outage | No assessment; preserve the streak. | Preserve until an actual park. |
1337
1404
  | New loop | Start at zero. | Start empty. |
1338
1405
 
@@ -1414,7 +1481,9 @@ completion before the model sees them in the next packet.
1414
1481
  WAIT without a live obligation continues to those results.
1415
1482
  This is operation recovery, not provider
1416
1483
  resampling. A malformed statement's Problem records the factual
1417
- `siblingsRetained: true` extension.
1484
+ `siblingsRetained: true` extension, and its row's `attrs` record the statement the parser refused,
1485
+ its `attempt` ({§parse-attempt}), outside the row's body and weight, so the packet shows the model
1486
+ nothing new.
1418
1487
 
1419
1488
  §invalid-emission-attempts Each exhausted emission-attempt budget contributes
1420
1489
  one frame-contract strike to the same streak as other contract violations
@@ -1658,7 +1727,7 @@ Every fact names the canonical key, never the host root or an echo of the
1658
1727
  model's spelling. These classes let a caller distinguish a wrong address, an
1659
1728
  invalid range, read-only authority, and occupied hidden state without guessing.
1660
1729
 
1661
- §membership-read-refusal **A file miss speaks of membership, never of the disk.** A file is read only as a member, so every `file` miss — READ, FIND of an exact path, KILL, a pattern EDIT's source, a COPY or MOVE source — is 404 `entry-not-found`, `No member of this workspace is at '<key>'.`, its target rendered as the reusable member address ({§fs-answer-in-canon}, {§file-url}). Recovery offers listing with FIND, creation with EDIT, and admission with `members (add)` as alternatives and presumes none ({§diagnostic-observation}): `` FIND lists paths. EDIT creates files; `members (add)` admits existing files with a `{"alias": "<name>", "definition": {"glob": "<path>"}}` body. `` The sentence is about the address and is true whether or not a file is there: it neither claims absence nor hints at presence. Beyond the root the engine does not look at the disk at all, so two reads of `../` paths differ only in the name they echo, and their recovery speaks of the address too, true under every members scope: `'<key>' is outside the project root: only a members definition under the operator's namespace scope admits it.` Inside the root occupancy is not secret ({§fs-write-nonmember}), so an exact-path READ of a path that exists on disk but is not a member says so instead — 404 `entry-not-member`, `'<key>' exists on disk but is not a member of this workspace.`, with admission-only recovery; a path the repository ignores, which no model definition admits, carries the members discovery verdict instead: `The repository ignores it: a client or operator members definition can include it, a model definition cannot.` Occupancy may surface there; content never does ({§membership}).
1730
+ §membership-read-refusal **A file miss speaks of membership, never of the disk.** A file is read only as a member, so every `file` miss — READ, FIND of an exact path, KILL, a pattern EDIT's source, a COPY or MOVE source — is 404 `entry-not-found`, `No member of this workspace is at '<key>'.`, its target rendered as the reusable member address ({§fs-answer-in-canon}, {§file-url}). Recovery offers listing with FIND, creation with EDIT, and admission with `members (add)` as alternatives and presumes none ({§diagnostic-observation}): `` FIND lists paths. EDIT creates files; `members (add)` admits existing files with a `{"files": ["<path>"]}` body. `` The sentence is about the address and is true whether or not a file is there: it neither claims absence nor hints at presence. Beyond the root the engine does not look at the disk at all, so two reads of `../` paths differ only in the name they echo, and their recovery speaks of the address too, true under every members scope: `'<key>' is outside the project root: only a members definition under the operator's namespace scope admits it.` Inside the root occupancy is not secret ({§fs-write-nonmember}), so an exact-path READ of a path that exists on disk but is not a member says so instead — 404 `entry-not-member`, `'<key>' exists on disk but is not a member of this workspace.`, with admission-only recovery; a path the repository ignores, which no model definition admits, carries the members discovery verdict instead: `The repository ignores it: a client or operator members definition can include it, a model definition cannot.` Occupancy may surface there; content never does ({§membership}).
1662
1731
 
1663
1732
  §file-directory-target **A directory is named as a directory.** Inside the root, a READ (or other exact-path read), KILL or EDIT whose target is a directory on disk — with or without a trailing slash — is refused `path-is-directory`, never as a missing or non-member file: READ and KILL answer 404, EDIT 403. The detail is `'<key>' is a directory, not a file; <OP> reads/removes/writes one file.`; a READ or KILL carries the listing as its recovery, `` `FIND (<key>/)` lists its files. ``, and an EDIT carries none. Beyond the root the disk stays dark and {§membership-read-refusal} holds unchanged.
1664
1733
 
@@ -1683,6 +1752,9 @@ cross-scheme SQL transaction. Core's create-only publication claims the same
1683
1752
  identity atomically: an existing identity returns 409 without changing its
1684
1753
  metadata or channels.
1685
1754
 
1755
+ Removing a channel preserves its siblings. Pruning the resulting entry requires
1756
+ it to be empty at the deletion boundary, not merely at an earlier observation.
1757
+
1686
1758
  ### Op methods
1687
1759
 
1688
1760
  §op-methods-op-dispatch Engine operation ownership follows the public scheme contract:
@@ -1700,7 +1772,7 @@ Registration precedes loop affinity:
1700
1772
  | Registered but inactive under flag | The flag gate returns `403 scheme-unavailable`. |
1701
1773
  | Registered and active | Dispatch continues to the operation owner. |
1702
1774
 
1703
- - §op-execution-order **An admitted turn is an ordered program.** Model, client, and harness operations execute in authored order. WAIT is deferred until all other admitted operations settle or establish their explicitly asynchronous work ({§disposition-anywhere}). The complete program then settles under {§wait-obligation-matrix}, whether or not it contains a lifecycle request; no completion operation or inventory is invented. Existing cycle, no-operation, and resource rails remain effective. An observation records the resource state at its execution point; the model sees that receipt in the next packet. Exact submitted source and actual operation outcomes remain durable. Earlier successful effects survive a later operation failure; a producer requesting fail-on-error stops before subsequent operations.
1775
+ - §op-execution-order **An admitted turn is an ordered program.** Model, client, and harness operations execute in authored order. WAIT is deferred until all other admitted operations settle or establish their explicitly asynchronous work ({§disposition-anywhere}). The complete program then settles under {§wait-obligation-matrix}, whether or not it contains a lifecycle request; no completion operation or inventory is invented. Existing cycle, no-operation, and resource rails remain effective. An observation records the resource state at its execution point; the model sees that receipt in the next packet. Exact submitted source and actual operation outcomes remain durable. Earlier successful effects survive a later operation failure; a producer requesting fail-on-error stops before subsequent operations. Cancellation stops before the next resolved operation, including remaining glob targets, while preserving already-settled effects, receipts, source, and provider accounting.
1704
1776
 
1705
1777
  §bare-inference **BARE is isolated, synchronous retrieval over the durable child-provider policy.**
1706
1778
 
@@ -1714,7 +1786,7 @@ Registration precedes loop affinity:
1714
1786
  | Failure | A source/admission/provider failure affects its own operation, not successful siblings. Accounting or persistence failure is internal and fails hard. |
1715
1787
  | Observation | Responses are unseen retrieval work; completion follows {§send-premature-terminate}. |
1716
1788
 
1717
- - §op-synchronous **Decisive operations settle before the next operation.** The dispatcher awaits each operation and its proposal resolution. Work remains in flight only when the operation's contract deliberately creates concurrency: FORK, WORK, a stream-producing execution, and streaming READ after acquisition. Such a READ first establishes its durable subscription and returns `102`; a later operation may address that live owner. Dispatching an execution before KILL does not wait for the process to finish using a resource. KILL of a worker synchronously ends its live loops before disposition checks the pending set; physical scope cleanup remains asynchronous.
1789
+ - §op-synchronous **Decisive operations settle before the next operation.** The dispatcher awaits each operation and its proposal resolution, including subsystem commands ({§functionality-model-mutation}). Work remains in flight only when the operation's contract deliberately creates concurrency: FORK, WORK, external execution, and streaming READ after acquisition. Such a READ first establishes its durable subscription and returns `102`; a later operation may address that live owner. Dispatching an external execution before KILL does not wait for the process to finish using a resource. KILL of a worker synchronously ends its live loops before disposition checks the pending set; physical scope cleanup remains asynchronous.
1718
1790
  - §edit-execution **One resolved EDIT target is one mutation.** Each EDIT resolves against current resource state when dispatch reaches it, owns its proposal when gated, and records its own resulting revision. No later EDIT is prepared or applied in advance. Numeric scopes address current coordinates; an earlier EDIT may change what those numbers select. Rejection applies only to that operation, not its successful siblings.
1719
1791
  - §edit-anchor-continuity **Own EDITs preserve untouched hash targets within one program.** Core carries an anchor through exact, successfully applied EDIT splices when its line survives unchanged, even if its ordinal or neighborhood changes. Scoped entry KILL uses the same deletion path. Target-line replacement or deletion invalidates that binding. Continuity is private to the admitted program and canonical resource/channel; it is not a new published anchor format. The complete normalized line content must match the expected result of the preceding recorded EDIT, otherwise retained bindings are discarded and ordinary current-state validation applies. Reviewer replacement and results without an applied EDIT receipt do not carry bindings forward. A retained binding resolves alone: a twin neighbourhood this program's own splice created elsewhere does not make the carried anchor ambiguous, and an anchor the program never bound resolves against current state. Normalization is the same line-content representation used by READ and line hashing; file-write revision checks remain independent. No approximate text matching is used. Lowered coordinates retain a current-anchor precondition at the mutation owner; ambiguous matches and concurrent changes remain collisions.
1720
1792
  - §anchor-offset **An anchor offset is tolerated, never taught (#749).** A line mark may carry an offset from its anchor (`@abcde+1`, `@abcde-2`), and a bare `+N` after an anchor counts from that anchor (`<@abcde,+1>`). The anchor resolves as usual and the offset is added; a result before line 1 is an invalid mark, and past the end is the ordinary range refusal. Continuity and current-anchor preconditions check the anchor's own line. `plurnk.md` keeps its two anchor forms. Numeric relative endpoints normalize under {§scope-range-recovery}; a reversed range produced by anchor resolution is never reinterpreted as a count.
@@ -2114,7 +2186,7 @@ source line therefore retains the same prefix across projections of one revision
2114
2186
  An explicit default-channel fragment and its fragmentless spelling share that
2115
2187
  identity; a selected non-default channel retains its canonical `#channel`.
2116
2188
 
2117
- For READ/LOOK and COPY/MOVE source or destination selection, core resolves every
2189
+ For READ and COPY/MOVE source or destination selection, core resolves every
2118
2190
  anchor against the addressed current complete content before applying the
2119
2191
  ordinary numeric text-coordinate contract. Exactly one current match lowers to
2120
2192
  its numeric line; zero or multiple matches return 409 `line-anchor-collision`,
@@ -2402,14 +2474,14 @@ nothing is reconstructed for them: such a row has no `patch` channel, and whatev
2402
2474
  rollback included, fails by name rather than inventing one.
2403
2475
 
2404
2476
  §rollback **Rollback reverses a worker's settled file mutations, latest first, through ordinary
2405
- operations.** `rollback({ workspaceId, workerId, clientWorkerId, to })` (`workspace.rollback { to,
2477
+ operations.** `rollback({ workspaceId, workerId, actor, to })` (`workspace.rollback { to,
2406
2478
  workerId? }` for a client) names a log coordinate of `workerId`, `<loop>/<turn>/<item>`; the state
2407
2479
  after that item is what remains. Every settled EDIT, COPY, MOVE or KILL row after it, latest first,
2408
- has its patch ({§log-mutation-patch}) reversed against the file as it is now and written back as an
2409
- operation of the acting client in `clientWorkerId`, settled under that worker's owner: a rewrite as
2410
- `EDIT <1,-1>`, a removed file as a creating `EDIT`, a created file as `KILL`. Each reversal is a
2411
- proposal, a row and a patch of its own, so a rollback is reviewable afterwards and reversible by the
2412
- same means. The report (`RollbackReport`) lists what was restored, the rows skipped because they
2480
+ has its patch ({§log-mutation-patch}) reversed against the file as it is now and written back within
2481
+ one client operation of `actor` ({§connection-lifecycle}), that person's decision already: a rewrite
2482
+ as `EDIT <1,-1>`, a removed file as a creating `EDIT`, a created file as `KILL`. Each reversal is a
2483
+ row and a patch of its own, so a rollback is inspectable afterwards and reversible by the same
2484
+ means. The report (`RollbackReport`) lists what was restored, the rows skipped because they
2413
2485
  kept no patch or were reversed already (the file is in the state before that mutation, as an earlier
2414
2486
  rollback left it), and where it stopped: `drift`, the file no longer holds what that mutation wrote,
2415
2487
  leaves that file and everything earlier untouched; `refused` carries the reversing operation's
@@ -2467,9 +2539,9 @@ complete non-retrieval body retains `lines` where no other field supplies its
2467
2539
  navigable extent. None of these spellings changes acquisition, delivery,
2468
2540
  curation, admission, or immutable evidence.
2469
2541
 
2470
- Field absence carries defaults: `origin` is omitted for the owning model, `source` for the owning worker, and `status` for a routine 200. An emission row renders its author, the turn's producer, as its `origin` ({§emission-row}). Dispositions always carry their lifecycle status, SEND its delivery status, KILL keeps an explicit 200, and every non-200 stays explicit. A present authored aside appears as `aside`. Every row's accounting follows {§packet-token-accounting}.
2542
+ Field absence carries defaults: `origin` is omitted for the owning model, `source` for the owning worker, and `status` for a routine 200, regardless of operation. Every non-200 stays explicit. This presentation rule does not alter message completion, durable results or protocol responses. An emission row renders its author, the turn's producer, as its `origin` ({§emission-row}). A present authored aside appears as `aside`. Every row's accounting follows {§packet-token-accounting}.
2471
2543
 
2472
- Authored `metadata` retains its opaque ordered block strings under {§scheme-metadata-modifier}; COPY/MOVE pair them as `{from,to}`. Packet rendering does not interpret scheme options or discard malformed input from a failed operation.
2544
+ Authored `metadata` retains its opaque ordered block strings under {§scheme-metadata-modifier}, as stored under {§log-sensitive-request-evidence}; COPY/MOVE pair them as `{from,to}`. Packet rendering does not interpret scheme options or discard malformed input from a failed operation.
2473
2545
 
2474
2546
  - §operation-resource-receipt A result's nonempty `resource` address remains visible in receipt metadata when distinct from its `path` and `stream`. It identifies returned material without replacing the addressed operand or injecting that material into context; ordinary READ acquires it.
2475
2547
  - §packet-attachment-parts A successful READ of an attachable resource carries projection facts with its
@@ -3011,7 +3083,12 @@ The notice creates no state and does not demand another final reply.
3011
3083
  §worker-obligations A worker holds its unresolved children, its open non-detached
3012
3084
  streams, and the messages it sent to other workers that await completion while the
3013
3085
  recipient's loop is live (`worker_obligations`); `loop_obligations` names them per
3014
- loop. The packet's Delegation list, WAIT, completion and drain wake settlement use
3086
+ loop. Each obligation records the operation that opened it, by identity: a stream its
3087
+ execution ({§subscriptions-causal-resource}), a message the SEND, WORK or FORK that sent it,
3088
+ by the row that operation writes; a child's loop is opened by its first message's. The
3089
+ opening row names what it opened: an execution's `attrs.stream`, a message's `attrs.message`
3090
+ (its durable address). A message holds, and its reply reaches, the worker whose own operation
3091
+ sent it; a message a person, a client or the daemon sends opens no worker's obligation. The packet's Open Work list, WAIT, completion and drain wake settlement use
3015
3092
  that same durable liveness: a worker that messaged another worker parks on an
3016
3093
  authored WAIT until the completion arrives ({§message-completion}), and with no WAIT
3017
3094
  joins that work with the bounded park rather than concluding idle.
@@ -3119,7 +3196,7 @@ accounting and model-visible failure evidence remain separately owned by
3119
3196
  The strike sends no notice of its own: the turn records one `_plurnk`
3120
3197
  error row, `422` `The turn performed no operation.`, which rides the next packet's errors like
3121
3198
  any failure ({§operation-result-uniform-error-channel}); the threshold terminal still says why ({§engine-rails}).
3122
- An empty turn uses its exact text as the cycle fingerprint ({§engine-cycle-evidence});
3199
+ An empty turn the output allowance did not cut uses its exact text as the cycle fingerprint ({§engine-cycle-evidence});
3123
3200
  different empty programs are not a repeated cycle merely because neither contained
3124
3201
  operations. Lost-boundary handling remains {§unparsed-tail-boundary}; no confirmation
3125
3202
  token or private retry is invented here.
@@ -3325,8 +3402,12 @@ unchanged for proposal policy, application, stream registration, and
3325
3402
  effect-qualified hold policy. The post-acceptance materialization path never
3326
3403
  triggers reclassification.
3327
3404
 
3328
- §exec-registry-resolves The runtime slot (`signal`) selects an executor from
3329
- the workspace snapshot. Installed siblings form the immutable base:
3405
+ §exec-registry-resolves The execution's `runtime` selects an executor from
3406
+ the workspace snapshot at its dispatch point. Explicit invocations parse without requiring
3407
+ registration ({§executor-heading}); a preceding accepted subsystem command may publish the
3408
+ name they invoke in that same program ({§functionality-model-mutation}). A failed or rejected
3409
+ command grants nothing; subsequent invocations resolve the unchanged snapshot, unless the
3410
+ producer requested fail-on-error and stopped the program. Installed siblings form the immutable base:
3330
3411
  they are discovered and probed at startup, and availability is cached.
3331
3412
  Workspace Functionality providers may atomically overlay additional names under
3332
3413
  {§module-workspace-capabilities}; a name has one owner within a workspace, while
@@ -3362,7 +3443,7 @@ execution is refused `scope-unsupported` (400).
3362
3443
  | `lifetime` | The spawn |
3363
3444
  | ------------ | --------- |
3364
3445
  | a duration | Aborted at the deadline — a bounded reap, polite signal then SIGKILL after `PLURNK_SERVICE_EXEC_KILL_GRACE_MS` — and the stream is stamped **504** `execution-timeout`, distinct from a deliberate kill (499) or a clean exit (200): `` Execution of '*runtime*' outlived its *lifetime* lifetime. `` |
3365
- | `loop` (absent) | Loop-life bounded: reaped on every loop terminal except 202, the background-stream behavior. |
3446
+ | `loop` (absent) | Loop-life bounded: reaped on every loop terminal except 202, the background-stream behavior. A person's client operation answers once its command starts, so its execution belongs to the operation instead: it outlives the operation loop's 200 and is reaped when that operation is cancelled or the daemon stops ({§connection-lifecycle}). |
3366
3447
  | `turn` | Reaped at the worker's next pre-turn via the registry abort, before the turn's own spawns, so it never survives into the subsequent turn; its terminal output surfaces born visible like any close ({§exec-stream}). |
3367
3448
  | `detached` | Outlives its loop's terminal, 200 included. It never binds to the loop's teardown and is nobody's obligation — completion is not gated by it, WAIT does not park on it, optimistic settlement looks past it — and it ends only by KILL, the worker's total reap, or daemon shutdown; its late conclusion surfaces without opening a loop. |
3368
3449
 
@@ -3408,18 +3489,53 @@ lifecycle teardown still reaps its work. No new delegation semantics follow.
3408
3489
 
3409
3490
  §worker-auto-name **Automatic names are eight random lowercase hexadecimal
3410
3491
  characters**, e.g. `worker://ab3d5678`; a colliding draw is retried. All unnamed
3411
- workers use this allocator, including WORK, FORK, clients, and conversations.
3492
+ workers use this allocator, including WORK, FORK, clients, and model workers.
3412
3493
  Names encode neither machine identity nor topology or role; those remain
3413
3494
  separate durable fields.
3414
3495
  Automatic names never reuse an existing literal. Name selection and worker
3415
3496
  creation share one atomic claim: concurrent allocations receive distinct
3416
- addresses, while concurrent default-conversation ensures converge on one root
3417
- worker. Generated addresses appear in ordinary operation receipts and child
3497
+ addresses. Generated addresses appear in ordinary operation receipts and child
3418
3498
  inventory; allocation never rewrites the submitted operation's target.
3419
3499
 
3420
- §workspace-auto-name **Workspace auto-names are five anchor-alphabet characters.** A `workspace.create` without a name draws five characters from the {§line-anchors} alphabet `0-9A-Za-z` (uniformly, from a cryptographic source), redrawing on the astronomically rare collision. No prefix, timestamp, or origin marker: a name never encodes where a workspace came from, so nothing can grow load-bearing on it.
3421
-
3422
- §exec-readpure-ungated A `read` runtime (observes external state, e.g. search) or `pure` runtime (no observable effect, e.g. `:memory:` sqlite) is side-effect-free → **auto-run**: no proposal, no human gate, no notification. Core persists the prepared operation before applying it; that write-ahead staging has no resolution waiter and therefore cannot enter proposal discovery ({§proposal-list}). It skips the gate a host command faces, but it does NOT resolve in-band — like every exec it backgrounds and streams, its output reaching the model through the environment-observation injector (a foisted READ of newly publishable stream content each turn, {§exec-stream}), never a same-turn receipt.
3500
+ §workspace-name **A workspace's name is an opaque handle; its identity is its id; its folder is a
3501
+ separate fact.** A name is a unique non-empty string compared by exact bytes, the same comparison for
3502
+ creation, rename and lookup; nothing in it is normalized or decoded, and it authorizes
3503
+ nothing: only `projectRoot` selects a folder, and a workspace may have none. Admission, at creation
3504
+ and rename, refuses these spellings as `name-invalid` (400):
3505
+
3506
+ - ASCII decimal digits alone (`[0-9]+`, including zero and leading zeroes): reserved for workspace IDs.
3507
+ - Text that is not well-formed Unicode: an unpaired surrogate has no encoding.
3508
+ - A slash-delimited segment exactly `.` or `..`: a URL parser resolves it away, encoded or not.
3509
+
3510
+ None is repaired or resolved. Every other spelling
3511
+ is admitted as written: spaces, Unicode, `~`, `@`, `%` and a literal `%2E%2E`, query and fragment
3512
+ punctuation, and a dot within a segment (`.config`, `file.txt`, `...`). A refused rename changes
3513
+ nothing. Admission governs new names only: a workspace already carrying a refused spelling keeps it,
3514
+ its id, its folder and its workers, stays listed and attachable, and is renamed like any other. Rename
3515
+ ({§methods-workspace-rename}) changes the name alone; {§methods-workspace-attach} selects an existing workspace. With a project
3516
+ root and no name, `workspace.create` names the workspace after the root, the root's path with the daemon's
3517
+ home abbreviated to `~` (`~` for the home itself, the absolute path outside it), and selects the
3518
+ workspace of that name when one exists instead of creating a second, so two default launches on the
3519
+ same project and daemon share one workspace and a concurrent pair resolves to one. The root is resolved
3520
+ before it is named, so a root spelled with `.` or `..` components derives the name of the folder it
3521
+ reaches and no default is ever refused; a root that is not well-formed Unicode is
3522
+ `project-root-invalid` (400), since it names no folder. The default is a
3523
+ convention, not a mapping: a renamed workspace is not re-selected by its old default, and a workspace may
3524
+ carry any name over any folder. An explicit name that exists is `409 name-conflict`, never a
3525
+ selection.
3526
+
3527
+ §workspace-label **A folder's display spelling is the daemon's.** Wherever the application port
3528
+ returns a workspace's folder it returns its label beside it: `projectLabel` on a client envelope and
3529
+ the `workspace/created` notice, `project_label` on a `listWorkspaces` row. The label is the folder as
3530
+ a default name spells it: the daemon's home is `~`, a path beneath it `~/<rest>`, and any other path,
3531
+ one that merely shares the home's prefix included, its absolute spelling. A workspace without a
3532
+ folder has a null label. It is derived from the folder on every read and never stored, and it never
3533
+ rewrites a name, so a client compares a workspace's name with its label by equality and derives
3534
+ neither from its own home.
3535
+
3536
+ §workspace-auto-name **A workspace with neither name nor project root gets five alphanumeric characters, at least one a letter.** Such a `workspace.create` draws uniformly from the {§line-anchors} alphabet `0-9A-Za-z` using a cryptographic source, redrawing digit-only names and collisions. No prefix, timestamp, or origin marker: a name never encodes where a workspace came from.
3537
+
3538
+ §exec-readpure-ungated A `read` runtime (observes external state, e.g. search) or `pure` runtime (no observable effect, e.g. `:memory:` sqlite) is side-effect-free → **auto-run**: no proposal, no human gate, no notification. Core persists the prepared operation before applying it; that write-ahead staging has no resolution waiter and therefore cannot enter proposal discovery ({§proposal-list}). Admission does not determine settlement: external executions background and stream, while subsystem commands settle under {§functionality-model-mutation}. Both expose output through ordinary environment observation ({§exec-stream}), not a second inline body path.
3423
3539
 
3424
3540
  §effect-policy-tunable **Effect admission is one knob per effect.**
3425
3541
  `PLURNK_SERVICE_EFFECT_HOST`, `PLURNK_SERVICE_EFFECT_READ` and
@@ -3435,7 +3551,7 @@ two states and no others:
3435
3551
 
3436
3552
  | state | what the model receives |
3437
3553
  |---|---|
3438
- | active | nothing in the Log. The `## Delegation` stream pointer reports timing and channel activity ({§child-orientation}); the model READs any range it wants, and every READ of a stream channel carries `terminal: false` while it runs and `terminal: true` once it has concluded, so an empty page is never mistaken for a finished command that printed nothing. |
3554
+ | active | nothing in the Log. The `## Open Work` stream pointer reports timing and channel activity ({§child-orientation}); the model READs any range it wants, and every READ of a stream channel carries `terminal: false` while it runs and `terminal: true` once it has concluded, so an empty page is never mistaken for a finished command that printed nothing. |
3439
3555
  | terminal | ONE `origin=_plurnk` READ at the execution's channel address, born visible, that is exactly a markerless READ of the channel — its bounded first page ({§read-selection-projection}, the whole channel when it fits, the channel's own mimetype), the `range` or `region`, terminal status and Problem, `terminal: true`, and any producer-supplied integer `exitCode`. The packet writes the read resource as its operand, exactly as an explicit READ does ({§log-address-metadata}). |
3440
3556
 
3441
3557
  §stream-observation-result **One liveness fact.** The durable READ result owns
@@ -3450,7 +3566,7 @@ publishes only once subscription settlement has installed its terminal result;
3450
3566
  until then the stream is still in flight and publishes on the turn after it settles.
3451
3567
 
3452
3568
  §exec-concurrency **Bounded admission per workspace (#389).** At most
3453
- `PLURNK_SERVICE_EXEC_CONCURRENCY` executions run at once in one workspace (`-1`
3569
+ `PLURNK_SERVICE_EXEC_CONCURRENCY` external executions run at once in one workspace (`-1`
3454
3570
  unbounded); the scope is the workspace, so neither delegation nor later turns
3455
3571
  bypass it and no other workspace can starve it. Every admitted execution still creates its
3456
3572
  entry, channels, and open subscription before its receipt returns, so queued work is
@@ -3462,7 +3578,8 @@ it: an immediate slot is `200 { outcome: "started" }`; delayed work is
3462
3578
  current truth. Admission is FIFO within the workspace; queue residence does not consume
3463
3579
  the execution timeout; a KILL while queued never invokes the executor and concludes the
3464
3580
  stream as any KILL does ({§stream-control}). The scheduler is the exec scheme's; the knob is the
3465
- service's ({§operator-config}), fail-hard on any other value.
3581
+ service's ({§operator-config}), fail-hard on any other value. Ordered subsystem
3582
+ commands use {§functionality-model-mutation}, not this background-work queue.
3466
3583
 
3467
3584
  §exec-stream-page **An unrequested delivery never exceeds the retrieval page.** The
3468
3585
  terminal observation is the same page a markerless READ returns, whatever the
@@ -3477,7 +3594,7 @@ records stop at the last complete line that fits, or at an exact Unicode region
3477
3594
  when even the first line exceeds the character bound. Explicit READ scopes stay exact.
3478
3595
 
3479
3596
  The durable per-subscription, per-channel cursor records the size last reported
3480
- to the model — by the Delegation stream pointer while active, by the terminal
3597
+ to the model — by the Open Work stream pointer while active, by the terminal
3481
3598
  observation at close — so the pointer can state growth and no partial document
3482
3599
  or record ever reaches the model. The terminal observation and its cursor
3483
3600
  transition commit atomically. A concluded stream lands one conclusion row per
@@ -3621,6 +3738,9 @@ Core derives the contracts-owned `ProposalProjection` from the durable proposed
3621
3738
  | Projection field | Durable authority |
3622
3739
  | --------------------- | ------------------------------------------------------------------------------------------------------------------ |
3623
3740
  | identity | proposed log row `id`, `worker_id`, `loop_id`, `turn_id`, and validated `op` |
3741
+ | `worker` | the worker's name ({§worker-name}) |
3742
+ | `coordinate` | the row's log coordinate `L/T/S` ({§methods-log-coordinate}) |
3743
+ | `expiresAt` | when the operator's decision deadline cancels the proposal ({§proposal-timeout-cancels}); absent for an untimed wait |
3624
3744
  | `target` | canonical `{ scheme, authority, pathname }` from `attrs.proposalTarget` for staged COPY/MOVE, otherwise the log row target |
3625
3745
  | `body` | proposed operation result `rx.body`; absent means the empty review body |
3626
3746
  | `attrs` | proposed log row `attrs` object |
@@ -3674,6 +3794,7 @@ operation, message sender, or attrs.
3674
3794
  | Runtime bookkeeping | reject, `runtime_bookkeeping` |
3675
3795
  | Server `accept` | accept |
3676
3796
  | Server `reject` | reject, `policy_veto` |
3797
+ | Server `review`, a person's client operation ({§connection-lifecycle}) | accept, `client_operation` |
3677
3798
  | Server `review`, owner implements `request_approval` | review |
3678
3799
  | Server `review`, owner has no reviewer | reject, `no_review_channel` |
3679
3800
 
@@ -3681,8 +3802,9 @@ Core selects the disposition once for the pending gate. Its live event and
3681
3802
  reconnect projection use that same decision. Automatic settlement precedes
3682
3803
  observational notification; an observer failure cannot change authority or orphan
3683
3804
  a waiter. Invalid configuration is diagnosed at the proposal boundary without
3684
- substituting authority ({§configuration-repair-path}). Client-authored
3685
- administrative operations retain their client's owner.
3805
+ substituting authority ({§configuration-repair-path}). A person's client operation is
3806
+ already that person's decision, so review never routes it; the server's `accept` and
3807
+ `reject` still apply.
3686
3808
 
3687
3809
  §runtime-bookkeeping-policy Runtime bookkeeping cannot acquire new authority.
3688
3810
  Generated reference publication and audit narration have no reviewer and do not
@@ -4126,6 +4248,13 @@ systemd user unit. Installation and enablement are explicit operator actions;
4126
4248
  package installation performs neither. The template documents executable-path
4127
4249
  and environment adjustments instead of introducing a service-management command.
4128
4250
 
4251
+ §nginx-example The service package ships `plurnk.nginx.conf`, an example nginx front, beside
4252
+ the unit and under the same terms. Hardening belongs there, not in the daemon: TLS, security
4253
+ headers, sign-in throttling by real client address and an optional network allow-list. The
4254
+ daemon answers every caller at the address it used ({§http-request-origin}); behind TLS it is
4255
+ told the public origin (`PLURNK_AGUI_ISSUER`, and `PLURNK_A2A_ENDPOINT_URL` when it exposes A2A),
4256
+ which it cannot see.
4257
+
4129
4258
  Model selection uses one selector vocabulary in `ProviderRegistry` ({§provider-instantiation}). `PLURNK_MODEL_<alias>=<provider>/<model-id>` optionally declares a friendly route and tuning scope; `PLURNK_MODEL=<selector>` selects either that alias or an exact provider/model route. `PLURNK_MODEL_CHILD=<selector>` uses the same vocabulary for the default child provider; unset means inherit the spawning loop's provider. Operator selections and alias declarations live in `.env`, not `.env.defaults`.
4130
4259
 
4131
4260
  Each knob's value lives on its panel and nowhere else (`plurnk-service config defaults` prints them all); this table says what the service's knobs mean.
@@ -4135,7 +4264,7 @@ Each knob's value lives on its panel and nowhere else (`plurnk-service config de
4135
4264
  | `PLURNK_SERVICE_DB_PATH` | SQLite file path; an explicit non-empty value overrides the derived default. |
4136
4265
  | `PLURNK_SERVICE_SQLITE_SYNCHRONOUS` | Connection-time WAL durability: `OFF`, `NORMAL`, `FULL` or `EXTRA` ({§db-durability}). |
4137
4266
  | `PLURNK_SERVICE_SHARE_FOLDER` | Parent of the shares written when no folder is named ({§share-folder}); empty is `$XDG_STATE_HOME/plurnk/shares`. |
4138
- | §operator-config-shared-keys `PLURNK_HOST`, `PLURNK_PORT` | The daemon-owned listener's bind address and TCP port; modules mount their paths on that one listener ({§http-host}). **A key the daemon and its clients both read has a shared owner**: `@plurnk/plurnk-contracts` declares these two and the optional `PLURNK_AGUI_URL` on its own panel, the one package every side depends on. The daemon folds it like any installed member's, a client folds it beneath its own, and so neither holds the other's default. The service's `--host` and `--port` flags are generated from that panel. |
4267
+ | §operator-config-shared-keys `PLURNK_HOST`, `PLURNK_PORT`, `PLURNK_AUTH`, `PLURNK_SECRET`, `PLURNK_PASS` | The daemon-owned listener's bind address and TCP port; modules mount their paths on that one listener ({§http-host}). The daemon's authentication posture, the operator's client secret and the operator's browser sign-in ({§credential-authority}, {§agui-oauth}). **A key the daemon and its clients both read has a shared owner**: `@plurnk/plurnk-contracts` declares these, and the optional `PLURNK_AGUI_URL`, on its own panel, the one package every side depends on. The daemon folds it like any installed member's, a client folds it beneath its own, and so neither holds the other's default. The service's `--host`, `--port`, `--auth`, `--user` and `--pass` flags are generated from that panel. |
4139
4268
  | §operator-config-git-ceiling `PLURNK_SERVICE_GIT_ALLOWED` | Hard service ceiling: only `1` admits Git membership and status; every other value denies them. |
4140
4269
  | §operator-config-file-create-scope `PLURNK_SERVICE_FILE_CREATE_SCOPE` | Hard file-creation ceiling: `none < root < namespace`. `none` denies new filesystem files, `root` admits only paths inside `project_root`, and `namespace` also admits canonical outside-root paths. Existing-member writes are unaffected. |
4141
4270
  | `PLURNK_SERVICE_FILE_MATERIALIZE_MAX_BYTES` | Byte ceiling in `1..104857600` for one workspace-file snapshot ({§membership-materialization-limit}). |
@@ -4322,7 +4451,7 @@ flowchart LR
4322
4451
  release --> warm["Bounded warm grace / idle LRU"]
4323
4452
  warm -->|new demand| lease
4324
4453
  warm -->|idle bound| cool["Deactivate providers;<br/>withdraw runtime projections"]
4325
- interface -->|worldless call| calls
4454
+ interface -->|daemon call| calls
4326
4455
  calls --> core["Core state and orchestration"]
4327
4456
  core --> events["subscribeToEvents<br/>(workspaceId, event, payload)"]
4328
4457
  events --> interface
@@ -4336,7 +4465,8 @@ modules in registration order. The daemon's listener is bound before durable adm
4336
4465
  ({§module-http-mounts}). Persisted workspaces with
4337
4466
  no durable work stay passive until first demand; activation publishes their
4338
4467
  complete capabilities and documentation before the demanding operation
4339
- proceeds. `setup` is the readiness boundary for every capability registered
4468
+ proceeds. An owner's arrival is such a demand ({§methods-workspace-activate}), so a first prompt
4469
+ waits only for what remains of it. `setup` is the readiness boundary for every capability registered
4340
4470
  with Core: recovery may demand a workspace provider before `start`. Requests on
4341
4471
  the listener remain unavailable until it is admitted; a module opens any other exterior
4342
4472
  ingress only after recovery. No
@@ -4377,11 +4507,12 @@ An invalid module object, factory result or lifecycle member is an implementatio
4377
4507
  not configuration, and fails loudly. What a module contributes registers through its owning public
4378
4508
  interfaces and is released during {§module-lifecycle} resource teardown.
4379
4509
 
4380
- §module-shutdown-order `Daemon.stop()` first rejects new capability demand and
4510
+ §module-shutdown-order `Daemon.stop()` first rejects new capability demand, cancels preparation
4511
+ under {§functionality-preparation-cancellation}, and
4381
4512
  aborts proposals, branches, derivations, and worker scopes. It begins module
4382
4513
  `stop()` calls in reverse registration order without serially awaiting them,
4383
4514
  so every producer is asked to stop even if another stalls. Core settles drains,
4384
- capability publications, module producers, streaming producers, derivations,
4515
+ whole workspace activation and cooling transitions, capability publications, module producers, streaming producers, derivations,
4385
4516
  mimetypes, schemes, and the final worker-settlement barrier while observers
4386
4517
  remain subscribed. Only then does it begin and join module `close()` calls in
4387
4518
  reverse order. Failures do not skip later phases and join one shutdown aggregate.
@@ -4392,6 +4523,9 @@ After asynchronous selection, the supervisor rechecks shutdown before creating
4392
4523
  a drain or installing a timer; parked-loop wake mutations also recheck worker
4393
4524
  cancellation under {§worker-lifecycle-durable-disposition}.
4394
4525
  The database remains available through observer closure and final maintenance.
4526
+ Residency transitions are retained from stop admission through settlement, including failed
4527
+ transitions; one failure does not stop the join of other workspaces. Expected preparation
4528
+ cancellation is not a shutdown failure; genuine preparation and cleanup failures are.
4395
4529
  The shared deadline bounds every phase, including observer delivery; forced
4396
4530
  shutdown may therefore lose notifications and reports the unfinished phase.
4397
4531
 
@@ -4412,7 +4546,8 @@ flowchart LR
4412
4546
  stop[Begin stop] --> abort[Abort core producers]
4413
4547
  stop --> moduleStop[Begin reverse module stop]
4414
4548
  abort --> drains[Settle worker drains]
4415
- drains --> joined[Settle module producers]
4549
+ drains --> residency[Join workspace transitions<br/>and capability publications]
4550
+ residency --> joined[Settle module producers]
4416
4551
  moduleStop --> joined
4417
4552
  joined --> producers[Settle streaming producers]
4418
4553
  producers --> resources[Dispose derivations,<br/>mimetypes, and schemes]
@@ -4429,6 +4564,8 @@ published module contract.
4429
4564
 
4430
4565
  | Setup function | Contract |
4431
4566
  |---|---|
4567
+ | `credentials` | The {§credential-authority} on the setup seam: the issuing module publishes its face (`publish({ issuer })`) before any module starts, and every module may `verify` through it. |
4568
+ | `httpAddress()` | The address of the listener the host bound before any module set up ({§http-host}), as on the application port: the issuing module derives its issuer from it at setup, so the face it publishes answers from publication. |
4432
4569
  | `registerRuntimes([{ decl, executor, availability, scheme? }, ...])` | Validates the complete canonical tag set under {§executor-runtime-declaration}, then publishes every process-wide executor and optional claimed scheme facet atomically. |
4433
4570
  | §module-workspace-provider `registerWorkspaceCapabilityProvider(namespaceOwner, provider)` | Registers one extension-unique Functionality provider. `activate({ workspaceId, retain })` reconstructs the workspace snapshot; idempotent `deactivate({ workspaceId })` releases process resources. Core coalesces demand and supplies residency leases for work that outlives its caller. |
4434
4571
  | §module-workspace-state `readWorkspaceModuleState(workspaceId, namespaceOwner)` | Reads one nullable JSON state value per workspace and provider. Core owns storage and lifecycle; the provider owns its schema. Store symbolic credential references, not copied secrets. A worker-scoped family's coordinator reads and replaces the same shape per worker in `worker_module_state` ({§functionality-scope}). |
@@ -4471,7 +4608,10 @@ Settling the coordinator includes accepted refreshes waiting for that gate,
4471
4608
  not only work already inside a family lane.
4472
4609
  Explicit client mutations use `try` and fail 409 before effects while the workspace is held.
4473
4610
  Refused mutations reserve no queue position; a client retry is a new admission.
4474
- An accepted model mutation uses `wait`, proceeding after its originating turn. Activation and turn-admission refresh use `none` inside
4611
+ An accepted operation mutation uses `wait`: its turn yields the shared lease
4612
+ at that operation, awaits exclusive publication, and reacquires its lease before
4613
+ dispatching the next operation. Concurrent publishers yield independently; no
4614
+ publisher waits while retaining the shared lease it needs released. Activation and turn-admission refresh use `none` inside
4475
4615
  their already-held demand boundary. Providers reject replacement while active
4476
4616
  requests, input exchanges, or Tasks depend on the old snapshot; infrastructure
4477
4617
  watches may be replaced. These are concurrency constraints, not creator privileges.
@@ -4493,8 +4633,11 @@ bound. Concurrent demand coalesces; cooling never closes a leased connection.
4493
4633
 
4494
4634
  Cooling withdraws live capability and releases process resources at the
4495
4635
  workspace boundary. Durable configuration, history, and saved entries remain.
4496
- New demand reconstructs the one shared snapshot. Shutdown cancels warm timers
4497
- and closes module resources through their owning provider.
4636
+ New demand reconstructs the one shared snapshot. Shutdown cancels warm timers and preparation,
4637
+ refuses new leases before database access, and joins whole activation and cooling transitions
4638
+ before releasing resources. An interrupted acquisition rejects with `AbortError`, never a usable
4639
+ lease. Cooling and activation cleanup remain joined, not cancelled.
4640
+ Cooling failures are reported; demand awaiting that transition receives its failure, not a lease.
4498
4641
 
4499
4642
  §module-workspace-residency-facet **Residency is the workspace's; processes are
4500
4643
  one family's facet.** The workspace is the lease unit, and every family's
@@ -4673,7 +4816,7 @@ state without demand; `workspace/preparation` broadcasts
4673
4816
  | Boundary | Visible state |
4674
4817
  |---|---|
4675
4818
  | Preparation begins | Family, `phase: preparing`, `alias: null`, UTC `since` |
4676
- | Adapter calls `progress(alias)` | Enabled alias being prepared; a fresh `since` |
4819
+ | Adapter reports the aliases it is preparing | One activity per alias, each keeping the `since` it began; an alias leaves when a later report omits it, so aliases prepared at once are listed together |
4677
4820
  | Prepared candidate enters publication | `phase: publishing`, `alias: null` |
4678
4821
  | Commit, rejection, or rollback settles | Family removed; empty array means no preparation |
4679
4822
 
@@ -4784,7 +4927,8 @@ reserved or masked name is no resource. The family's manager exposes it as the f
4784
4927
  the value its commands receive now, FIND lists the names in effect and a pattern on it matches
4785
4928
  values, COPY carries a value out. The manager's own output streams, eight hexadecimal characters
4786
4929
  ({§execution-output-identity}), stay the executor's. A write is the Worker's own layer, never the
4787
- workspace's or the operator's: EDIT sets the whole value through the family's `replace` for the
4930
+ workspace's or the operator's (the runtime worker keeps none, so a person's write is the workspace's,
4931
+ {§actor-boundary-client-operations}): EDIT sets the whole value through the family's `replace` for the
4788
4932
  Worker, and KILL retires the name from the Worker's commands — `remove` for an entry the Worker
4789
4933
  owns, which restores an inherited value, `disable` for an inherited one, which keeps its
4790
4934
  definition. Admission is the family's ({§env-functionality}) and its refusal is the operation's
@@ -4796,7 +4940,9 @@ effect is `entry-not-found` (404) on READ and KILL alike.
4796
4940
  §functionality-scope **A family declares its supported scopes.** Skills, MCP, members
4797
4941
  and outbound A2A describe what exists in a **workspace**: a capability, resident or installable,
4798
4942
  that every Worker there shares. Environment describes how one **Worker** works — context rather
4799
- than capability — with worker overrides above workspace defaults. The adapter declares
4943
+ than capability — with worker overrides above workspace defaults. A context family's definitions are read where they
4944
+ are used, so a change at either scope holds nothing resident and takes no workspace exclusivity: it serializes on
4945
+ the family's lane. The adapter declares
4800
4946
  `scopes` (absent means `["workspace"]`; the first is the model default), and the
4801
4947
  coordinator keys durable state and the locally-owned `origin`
4802
4948
  by it: a workspace-scoped family's own entries carry origin `workspace`, a worker-scoped
@@ -4883,20 +5029,27 @@ A family that ships no file has a header-only document. Registration also
4883
5029
  validates the adapter's taught `add` example against the `add` input schema it
4884
5030
  teaches, so a wrong example fails boot rather than the model.
4885
5031
 
4886
- §functionality-model-mutation **An accepted mutation completes through its ordinary
4887
- execution stream.** Preparation and publication share the family's serialized
4888
- lane. Publication acquires workspace exclusivity after current turns release
4889
- their leases; the invoking stream remains pending until publication completes.
5032
+ §functionality-model-mutation **Subsystem commands settle in authored order.**
5033
+ Every manager verb, including inspection, completes before dispatch proceeds;
5034
+ later operations observe earlier successful configuration changes. These
5035
+ core-owned commands use ordinary proposals and durable execution outputs, but
5036
+ not the background-execution slot pool. Preparation and publication share the
5037
+ family's serialized lane. Resident publication uses the lease handoff in
5038
+ {§module-workspace-quiescence}; context-only families retain their existing
5039
+ scope-local serialization. The invoking operation and its output remain pending
5040
+ until settlement; they never acknowledge merely starting a future commit.
4890
5041
  It then reports `active`, `unavailable`, or `authorization-required`, or the exact
4891
5042
  publication failure. Resource readiness is not execution liveness: a mutation's
4892
5043
  `202 authorization-required` body remains exact, while the finished manager
4893
5044
  execution closes with `200`. It does not wait for sign-in. A preparation failure may publish enabled-but-unavailable
4894
- state for other mutations, but `replace` rejects it ({§functionality-replacement}); a publication failure never reports a successful mutation. Stream polling,
4895
- waiting, and result observation use the ordinary execution lifecycle, without a
5045
+ state for other mutations, but `replace` rejects it ({§functionality-replacement}); a publication failure never reports a successful mutation. Result observation uses the ordinary execution lifecycle, without a
4896
5046
  separate deferred-commit queue. An explicit client action publishes now, rejects
4897
5047
  a failed preparation, and fails 409 while the workspace is held
4898
5048
  ({§module-workspace-quiescence}). Rejecting a proposal prepares, persists, and
4899
- publishes nothing.
5049
+ publishes nothing. Cancellation removes queued publication and aborts preparation
5050
+ before publication; an already-started atomic publication is joined, never
5051
+ abandoned partway through. Shell commands, remote tools, WORK/FORK and streaming
5052
+ READ retain their deliberately asynchronous contracts ({§op-synchronous}).
4900
5053
 
4901
5054
  ## §methods Application interface
4902
5055
 
@@ -4908,35 +5061,36 @@ Core's behavior behind them.
4908
5061
  | Area | Function | Core contract |
4909
5062
  |---------------------------------------------------|----------|---------------|
4910
5063
  | §methods-event-subscribe Events | `subscribeToEvents(handler) -> unsubscribe` | Subscribes to the raw event source in {§notifications}. A subscriber failure is logged and cannot re-enter engine control flow. |
5064
+ | §methods-credentials Credentials | `credentials` | The {§credential-authority}: `issue`, `verify`, `consume`, `revokeFamily`, `onRevoked` and the sign-ins of a subject by `sessions` ({§credential-sessions}), and a module's own client by `registerClient`, `client` and `authenticateClient` ({§credential-module-client}); one in-memory authority per daemon, offered to every module on the seam. |
4911
5065
  | §proposal-list Proposals | `pendingProposals(workspaceId)` | Intersects durable proposed rows with the lifecycle owner's live resolution waiters, then returns their validated {§proposal-projection}; persistence alone cannot advertise an unresolvable client interrupt. |
4912
5066
  | §methods-proposal-resolve Proposals | `resolveProposal(logEntryId, resolution, owner)` | Validates the workspace-scoped owner before delivering one accept, reject, or cancel decision. Unknown, mismatched, and already-resolved identities fail without consuming the gate; the adapter authenticates its owner identity. |
4913
5067
  | §client-interaction-list Client interactions | `pendingClientInteractions(workspaceId)` | Intersects durable interaction rows with their live operation waiters and returns the contracts-owned projection; a row alone is not a resumable interaction. |
4914
5068
  | §methods-client-interaction-resolve Client interactions | `resolveClientInteraction(interactionId, resolution, respondent, message?)` | Validates the recorded recipient before delivering one resolved payload or cancellation. Unknown, mismatched, and already-resolved identities fail before affecting an operation. |
4915
5069
  | §methods-loop-run Loops | `runLoop({ workspaceId, workerId, loopId?, prompt, source?, maxTurns?, openPaths?, selector?, childSelector? })` | Validates a model worker and persists the request with the effective turn ceiling, then returns an immediate status-100 acknowledgement with `loopId` and `action`. An explicit Loop recipient follows {§loop-addressed-admission}. A trusted adapter may identify the prompt's causal actor with one canonical `source`; ordinary clients cannot author it through their protocol surface. Messages carry no approval authority. The exact terminal result arrives only through `loop/terminated`; parking and resuming do not replace the loop. |
4916
- | §methods-loop-cancel Loops | `cancelDrain(workerId, reason?)`; `cancelWorker({ workspaceId, workerId, reason? })` | `cancelDrain` begins durable structured cancellation and reports whether process-local work existed when called; queued or parked durable work is still terminalized when it is `false`. The ownership-bounded `cancelWorker` awaits that same tree cancellation and stream reap, so an exterior protocol can project the settled durable result without polling or fabricating state. |
4917
- | §methods-op-mirror Client dispatch | `dispatchClientAction({ workspaceId, workerId, statements })` | Dispatches already-parsed grammar statements as one client action in one administrative loop in the client worker, executing in the workspace's Functionality ({§actor-boundary-attached-functionality}). Every statement is an ordered client/operation turn, and every committed `log/entry` is emitted before the action promise resolves; a proposal may keep its turn, loop, and action promise open until resolution. Core exposes no per-op method family. |
4918
- | Client observation | `look({ workspaceId, workerId, statement, perspectiveWorkerId? })` | Runs an already-parsed READ through the full resolver in the workspace's Functionality without a log row. A non-READ statement is rejected ({§op-look}). |
4919
- | §methods-log-read Reads | `readLog({ workspaceId, workerId, ...coordinate })` | Ownership-checks the worker, then reads by ids, recency, or the complete `loopSeq`/`turnSeq`/`sequence` display coordinate. An omitted `limit` is `PLURNK_SERVICE_LOG_READ_PAGE`, and any `limit` is capped at `PLURNK_SERVICE_LOG_READ_MAX`. |
5070
+ | §message-idempotency Message admission | `messageAdmission({ workspaceId, address, body })` | A client message's address is its idempotency key ({§message-source-scheme}): `new` when nothing is accepted there, `accepted` when the same body is, so a repeat attaches to what the message started instead of admitting it twice; another body under an accepted address is refused, 422 `message-address-reused`, creating nothing. `runLoop` refuses every repeat it is handed: other words 422 `message-address-reused`, the same words 409 `message-already-accepted`, a duplicate racing the original. |
5071
+ | §methods-loop-cancel Loops | `cancelDrain(workerId, reason?)`; `cancelWorker({ workspaceId, workerId, reason? })`; `cancelLoop({ workspaceId, loopId, reason? })` | `cancelDrain` begins durable structured cancellation and reports whether process-local work existed when called; queued or parked durable work is still terminalized when it is `false`. The ownership-bounded `cancelWorker` awaits that same tree cancellation and stream reap, so an exterior protocol can project the settled durable result without polling or fabricating state. `cancelLoop` cancels exactly one client operation ({§connection-lifecycle}): its dispatch while that runs, and every attached execution its rows started that still runs, then reports whether any of it was running; nothing else in the runtime worker shares its scope. |
5072
+ | §methods-op-mirror Client dispatch | `dispatchClientAction({ workspaceId, actor, contextWorkerId?, statements, onLoop? })` | Dispatches already-parsed grammar statements as one client operation of `actor` ({§connection-lifecycle}), in the workspace's Functionality ({§actor-boundary-attached-functionality}), reading `log:///` in `contextWorkerId`'s log ({§actor-boundary-client-operations}). `onLoop` hears the operation's loop before any statement runs. Every statement is an ordered client/operation turn, and every committed `log/entry` is emitted before the promise resolves. Core exposes no per-op method family. |
5073
+ | §methods-log-read Reads | `readLog({ workspaceId, workerId, ...coordinate })` | Ownership-checks the worker, then reads by ids, recency, or the complete `loopSeq`/`turnSeq`/`sequence` display coordinate, newest first. `sinceId` keeps rows after an id and `beforeId` rows before one, so a reader pages back through the whole log one page at a time. An omitted `limit` is `PLURNK_SERVICE_LOG_READ_PAGE`, and any `limit` is capped at `PLURNK_SERVICE_LOG_READ_MAX`. |
4920
5074
  | §methods-entry-read Reads | `readEntry({ workspaceId, workerId, target, channel?, offset? })` | Resolves the selector from that worker's perspective and returns {§entry-read-result}, either complete or as one channel suffix, without creating action evidence. |
4921
5075
  | Providers | `listProviders()` | Lists configured aliases with provider/model identity, active state, and the effective provider-derived `inputCapacity` when known. |
4922
5076
  | Model catalog | `listModels(query)` | Returns one validated bounded {§model-catalog-wire} page under {§model-catalog}; performs no provider request or selection. |
4923
5077
  | Client capabilities | `listClientDisplayCapabilities()` | Composes sorted scheme declarations ({§manifest-client-display}) followed by sorted MIME declarations ({§mimetype-client-display}) into the validated shared wire ({§client-display-capabilities}). The internal `exec` operation handler is excluded; its addressable runtime-tag scheme faces remain included. |
4924
- | §methods-workspace-create Workspace lifecycle | `createWorkspace({ name?, projectRoot?, settings? })` | Validates `settings` through {§operator-config-workspace-settings}, creates the world and its client envelope, and emits global `workspace/created`. Creation and attachment are passive: neither starts derivation nor activates workspace Functionality. `projectRoot` is established here or the workspace remains headless. |
4925
- | §methods-workspace-attach Workspace lifecycle | `attachWorkspace({ workspaceId, workerId?, workerName? })` | Validates ownership and returns a client envelope for an existing world. It does not retain caller or transport binding state in core. |
4926
- | §methods-model-worker Workspace lifecycle | `ensureModelWorker(workspaceId)` | Returns the workspace's stable default model worker, creating it on first use. A durable default-conversation role identifies it independently of worker name and root creation order. Repeated and concurrent calls return the same root; fresh conversations and forks do not replace it. |
4927
- | §methods-conversation-worker Workspace lifecycle | `createConversationWorker({ workspaceId, name?, parentWorkerId?, owner? })` | Creates a distinct model-origin worker with empty history. A root may name a registered owner; a child names its parent and inherits ownership. Supplying both parent and owner is invalid ({§worker-owner-creation}). |
5078
+ | §methods-workspace-create Workspace lifecycle | `createWorkspace({ name?, projectRoot?, settings? })` | Validates `settings` through {§operator-config-workspace-settings}, creates the workspace and returns its client envelope, minting no worker, and emits global `workspace/created`; with a project root and no name it names and selects the workspace under {§workspace-name}, emitting `workspace/created` only for a workspace it created. Creation and attachment are passive: neither starts derivation nor activates workspace Functionality. `projectRoot` is established here or the workspace remains headless. |
5079
+ | §methods-workspace-attach Workspace lifecycle | `attachWorkspace({ workspaceId } \| { name } \| { projectRoot })` | Exactly one selector returns an existing workspace's client envelope, minting and activating nothing. Names are literal lookups, including previously admitted names. An absolute project root selects its folder-derived default name ({§workspace-name}), not a workspace by its stored folder. An absent target is `404 workspace-not-found`; ambiguous or absent selectors are `400 identity-invalid`. It retains no caller or transport binding state. |
5080
+ | §methods-workspace-activate Workspace lifecycle | `activateWorkspace({ workspaceId })` | Makes the demand a turn makes, without a turn: a cold workspace activates and publishes its complete capabilities; a concurrent demand shares that one activation; it resolves once the workspace is active and leaves it warm under `PLURNK_SERVICE_WORKSPACE_WARM_MS`. Creation and attachment stay passive; an owner's arrival calls it ({§agui-owner-activation}). |
5081
+ | §methods-worker-create Workspace lifecycle | `createWorker({ workspaceId, name?, parentWorkerId?, owner? })` | Creates a distinct model-origin worker with empty history. A root may name a registered owner; a child names its parent and inherits ownership. Supplying both parent and owner is invalid ({§worker-owner-creation}). |
4928
5082
  | Worker ownership | `registerWorkerOwner(workspaceId, { address, tools, interactive })` | Declares or replaces an owner's supported client-tool names and whether a person attends it, in that workspace; `_plurnk` is reserved. |
4929
5083
  | Worker ownership | `claimWorkerOwner({ workspaceId, workerId, owner })` | Claims only runtime-owned work under {§worker-ownership}, returning its effective owner. The owner must already be registered. |
4930
5084
  | Runtime actor | `ensureRuntimeWorker(workspaceId)` | Returns the workspace's `_plurnk` actor, creating it if absent; it always retains runtime ownership. |
4931
- | Workspace lifecycle | `forkWorker({ workspaceId, workerId, name? })` | Creates a child worker that branches the source worker's history while sharing workspace state. |
4932
- | §methods-workspace-rename Workspace metadata | `renameWorkspace(workspaceId, name)` | Changes only the world's unique mutable name; workers, log, and membership remain intact. |
4933
- | §methods-workspace-rollback Workspace files | `rollback({ workspaceId, workerId, clientWorkerId, to })` | Reverses `workerId`'s settled file mutations after its coordinate `to` as the client's own operations in `clientWorkerId` ({§rollback}); answers a `RollbackReport`. |
4934
- | §methods-workspace-prompts Workspace metadata | `listPrompts(workspaceId, limit?, workerId?)` | Returns nonempty loop-seed prompts a client addressed to the workspace's model workers, newest-first; `workerId` narrows to one worker. Authorship is the seed message's address ({§message-arrival}): a worker-issued seed (WORK, FORK, SEND to a worker) has none and is never history, whichever worker it seeded; a client prompt at a forked conversation worker is. An omitted limit is `PLURNK_SERVICE_PROMPTS_PAGE`. |
4935
- | Workspace metadata | `listWorkspaces()`, `workspaceDerivationStatus(...)` | Reads current workspace identity and derivation progress. |
4936
- | §methods-worker-read Worker topology | `readWorker({ workspaceId, identity })` | Ownership-bounds an exact id-or-name lookup and returns one durable Worker projection or `null` under {§application-worker-observation}. Supplying both identities or neither is invalid. |
4937
- | §methods-worker-list Worker topology | `listWorkers(workspaceId, query?)` | Returns the workspace's durable Worker projections under {§application-worker-observation}. The origin filter is exact; an explicitly present `parentWorkerId` filters roots (`null`) or one immediate parent (id), while omission returns every lineage position. Each projection carries `kind` (`conversation`, `fork` for a child with a fork boundary, `work` for any other child) and `lifecycle`, the representative work loop's status through {§loop-lifecycle-vocabulary} (`idle` with no work loop), so a directory row shows the same lifecycle glyph the bound worker's own status gauge shows; clients infer neither (#523). |
4938
- | §methods-worker-loops Loop lifecycle | `listWorkerLoops({ workspaceId, workerId })` | Ownership-checks the Worker and returns its Loops in sequence order under {§application-loop-observation}, including the validated exact terminal result when one exists. It performs no scheduling or event replay. |
4939
- | §methods-worker-descendants Descendant spend | `descendantAccounting({ workspaceId, workerId, loopId })` | Ownership-checks the Worker and returns the {§provider-accounting} projection of every settled request on a loop that a descendant of the Worker (`parent_worker_id`, to any depth) ran after `loopId` — this delegation's spend, the same tree the turn cap counts ({§turn-cap-counts-the-tree}). The Worker's own loop is never included: its accounting stays {§notifications-loop-terminated}'s. `loopId: null` is the empty projection, explicit zero. Derived from the request ledger on every read ({§tokenomics-provider-usage}); no rollup, no second store. |
5085
+ | Workspace lifecycle | `forkWorker({ workspaceId, workerId, name? })` | Creates a child worker that branches the source worker's history while sharing workspace state. The source is a model worker, else `409 model-worker-required` ({§machine-processes-worker-origin}). |
5086
+ | §methods-workspace-rename Workspace metadata | `renameWorkspace(workspaceId, name)` | Changes only the workspace's unique mutable name, under the admission of {§workspace-name}; workers, log, and membership remain intact, and a refused name changes nothing. |
5087
+ | §methods-workspace-rollback Workspace files | `rollback({ workspaceId, workerId, actor, to })` | Reverses `workerId`'s settled file mutations after its coordinate `to` as one client operation of `actor` ({§rollback}, {§connection-lifecycle}); answers a `RollbackReport`. |
5088
+ | §methods-workspace-prompts Workspace metadata | `listPrompts(workspaceId, limit?, workerId?)` | Returns nonempty loop-seed prompts a client addressed to the workspace's model workers, newest-first; `workerId` narrows to one worker. Authorship is the seed message's address ({§message-arrival}): a worker-issued seed (WORK, FORK, SEND to a worker) has none and is never history, whichever worker it seeded; a client prompt at a forked model worker is. An omitted limit is `PLURNK_SERVICE_PROMPTS_PAGE`. |
5089
+ | Workspace metadata | `listWorkspaces()`, `workspaceDerivationStatus(...)` | Reads current workspace identity, with each folder and its label ({§workspace-label}), and derivation progress. |
5090
+ | §methods-worker-read Worker topology | `readWorker({ workspaceId, identity })` | Ownership-bounds an exact id-or-name lookup and returns one durable Worker projection or `null` under {§application-worker-observation}. Supplying both identities or neither is invalid. Any non-empty name is looked up, the runtime worker's `_plurnk` included: the lookup is no minting door ({§worker-name-minting}). |
5091
+ | §methods-worker-list Worker topology | `listWorkers(workspaceId, query?)` | Returns the workspace's durable Worker projections under {§application-worker-observation}. The origin filter is exact; an explicitly present `parentWorkerId` filters roots (`null`) or one immediate parent (id), while omission returns every lineage position. Each projection carries `kind` (`root` for a worker with no parent, `fork` for a child with a fork boundary, `work` for any other child) and `lifecycle`, the representative work loop's status through {§loop-lifecycle-vocabulary} (`idle` with no work loop), so a directory row shows the same lifecycle glyph the bound worker's own status gauge shows; clients infer neither (#523). |
5092
+ | §methods-worker-loops Loop lifecycle | `listWorkerLoops({ workspaceId, workerId })` | Ownership-checks the Worker and returns its Loops in sequence order under {§application-loop-observation}, including the validated exact terminal result when one exists, and each Loop's `invocation`: `{ workerId, address }`, the Worker and the log address ({§methods-invocation}) of the operation that sent the Loop's first message ({§worker-obligations}), or null when a person, a client, a peer or the daemon sent it. It performs no scheduling or event replay. |
5093
+ | §methods-worker-accounting Worker spend | `workerAccounting({ workspaceId, workerId })` | Ownership-checks the Worker and returns the {§provider-accounting} projection of every settled request on any loop the Worker or a descendant of it (`parent_worker_id`, to any depth) ran, since the Worker began: its own spend and its delegations', the same tree the turn cap counts ({§turn-cap-counts-the-tree}). Nothing spent is explicit zero. One loop's own accounting stays {§notifications-loop-terminated}'s. Derived from the request ledger on every read ({§tokenomics-provider-usage}); no rollup, no second store. |
4940
5094
  | Extension actions | `listModuleActions()`, `invokeModuleAction(name, params, context)` | Lists setup-registered `{ name, scope, inputSchema, outputSchema }` descriptors in sorted order. Invocation requires a context matching the registered scope; missing names, forged scope, and missing workspace identity fail before the owner runs. Handler values remain opaque to core. |
4941
5095
 
4942
5096
  §methods-loop-run-fold-consistency **A folded prompt cannot silently reconfigure
@@ -4972,19 +5126,17 @@ If an unpublished message is promoted into subsequent work under
4972
5126
  {§message-loop-containment}, its selected paths travel with it.
4973
5127
 
4974
5128
  §methods-rebind **Binding belongs to the client-interface module.** Core's
4975
- workspace lifecycle calls return exactly the workspace and selected client actor
4976
- (`workspaceId`, `workspaceName`, `projectRoot`, `workerId`, `workerName`); they
4977
- carry no conversation-worker or action-loop binding. Core retains no connection,
4978
- thread, or current-workspace mapping. A module may replace its own binding with a
4979
- later create or attach result without requiring a new transport; it resolves the
4980
- conversation worker separately, while each client action allocates its own
4981
- administrative loop under {§connection-lifecycle}.
4982
-
4983
- §methods-worker-name-admission **Client worker-name admission.** Attach,
4984
- fresh-conversation, and fork apply {§worker-name-minting} before lookup or
4985
- creation. A client therefore cannot forge or resume the runtime actor (its
4986
- name lies outside `WORKER_NAME`), insert a non-mintable spelling, or make the
4987
- client registry diverge from model worker control.
5129
+ workspace lifecycle calls return exactly the workspace (`workspaceId`,
5130
+ `workspaceName`, `projectRoot`, `projectLabel`); they carry no worker, bound-worker
5131
+ or operation-loop binding. Core retains no connection, thread, or current-workspace
5132
+ mapping. A module may replace its own binding with a later create or attach result
5133
+ without requiring a new transport; it resolves the bound worker separately, while
5134
+ each client operation allocates its own administrative loop under {§connection-lifecycle}.
5135
+
5136
+ §methods-worker-name-admission **Worker-name admission.** Worker
5137
+ creation and fork apply {§worker-name-minting} before lookup or creation. A client therefore cannot forge or resume the runtime actor (its
5138
+ name lies outside `WORKER_NAME`) or create a worker under a spelling model worker control
5139
+ could not mint.
4988
5140
 
4989
5141
  §capability-admission **One admission path owns external authority.** Core
4990
5142
  derives one or more `CapabilityDescriptor` demands from each routed statement,
@@ -5021,7 +5173,7 @@ resolver.** Workspace capability actions return the contracts-owned
5021
5173
  their normalized effective intersection. The same resolver governs dispatch
5022
5174
  and packet shaping. Replacement preserves unrelated workspace settings. Before their next operation
5023
5175
  or observation, existing workers reconcile generated references against it. It requires no selected
5024
- conversation worker and returns the complete fresh projection.
5176
+ bound worker and returns the complete fresh projection.
5025
5177
 
5026
5178
  §question-tool **The native request-user-input tool.** Core registers one
5027
5179
  in-process `question` runtime at boot and offers it only to a worker whose owner
@@ -5084,7 +5236,7 @@ packets or state snapshots.
5084
5236
  owns one durable model, persisted as a nullable `model_routes` foreign key.
5085
5237
  Concurrent first selections of the same resolved tuple share one identity;
5086
5238
  an insertion conflict returns the existing route without changing its fields.
5087
- The root conversation worker is seeded once — from an explicit selection, else
5239
+ A root model worker is seeded once — from an explicit selection, else
5088
5240
  the daemon default — and never re-seeded from a later default change. A
5089
5241
  deliberately modelless daemon leaves the worker unset and rejects model work
5090
5242
  until an explicit selection. Starting a loop snapshots the worker's resolved
@@ -5093,7 +5245,7 @@ continue from the loop snapshot and never re-resolve through the alias
5093
5245
  cascade. A WORK/FORK child copies the spawning loop's effective spawn model
5094
5246
  (spawn override ?? model) onto the new worker by value at creation; it retains
5095
5247
  no live link and begins with no override, so a later parent change affects
5096
- only that worker's future loops and descendants. Client operation actors and
5248
+ only that worker's future loops and descendants. The runtime worker and other
5097
5249
  Plurnk-owned bookkeeping workers run no model loops and own no model
5098
5250
  selection; the model, spawn-override, and effort controls refuse them with
5099
5251
  `409 model-worker-required` before any policy row is initialized or written. An explicit model, spawn-override, or effort change while
@@ -5175,26 +5327,40 @@ beside database ids, so a client can render and resolve the logical `L/T/S`
5175
5327
  coordinate without fetching all rows and matching locally.
5176
5328
 
5177
5329
  §methods-log-entry-wire **Log entry wire fidelity.** `readLog` and `log/entry`
5178
- preserve causal `source` and parse the row's JSON `attrs` into structured data.
5330
+ preserve causal `source` and the row's lifecycle `state` (`proposed` while a proposal awaits its
5331
+ decision, then `resolved`, `failed` or `cancelled`), and parse the row's JSON `attrs` into structured data.
5179
5332
  Client interfaces do not reconstruct these fields from operation or origin.
5180
5333
 
5181
5334
  §methods-readable-reasoning **Readable provider reasoning remains derived
5182
- provider evidence.** On model SEND and disposition rows, `readLog` and `log/entry`
5183
- project a nonempty admitted `packet.assistant.reasoning` as the optional
5184
- `reasoning` field. The durable packet remains the sole stored representation;
5335
+ provider evidence.** On a turn's first model operation row, `readLog` and `log/entry`
5336
+ project the turn's nonempty admitted `packet.assistant.reasoning` as the optional
5337
+ `reasoning` field, from the row's first announcement; no other row repeats it. Beside it,
5338
+ `reasoning_model_call_id` and `reasoning_request_sequence` name the request that streamed it (the
5339
+ turn's accepted model call and that call's response request), so a client can match the reasoning it
5340
+ watched stream; history a FORK inherited was streamed by no request of this worker and names none. The durable packet remains the sole stored representation;
5185
5341
  core does not copy readable reasoning into log attributes or bodies. A turn
5186
5342
  without readable reasoning omits the field.
5187
5343
 
5188
- §op-look **LOOK ownership.** A client-interface module owns the public LOOK
5189
- spelling and grammar parsing. It rewrites a valid LOOK statement to READ and
5190
- hands the AST to core's `look`; core owns the full resolver and the no-log
5191
- invariant. The internal closed, rowless observation segment supplies an honest
5192
- numeric loop coordinate for relative `log:///` addressing without leaving
5193
- active lifecycle behind. The segment belongs to the acting worker (`workerId`);
5194
- the READ resolves `log:///` as `perspectiveWorkerId` when one is given; explicit source
5195
- authorities retain their identity under {§turn-source-resources}. A client can look at a conversation without
5196
- adding a loop to it. LOOK text anchors resolve through the same
5197
- {§line-anchors} path as READ.
5344
+ §methods-invocation **A row that discharges an obligation names the operation that opened it.** `readLog`
5345
+ and `log/entry` project `invocation`, the log address of that operation ({§worker-obligations}), on every
5346
+ row that discharges one, so a client pairs the answer with its call without matching addresses:
5347
+
5348
+ | Discharging row | Its invocation |
5349
+ |---|---|
5350
+ | A stream observation ({§exec-stream}) | The execution, as its subscription recorded it ({§subscriptions-causal-resource}) |
5351
+ | A child's conclusion ({§env-delta-child-termination}) | The operation that sent the first message of the concluded loop |
5352
+ | A completion reply delivered to the sender ({§message-reply-delivery}), including {§message-abandonment} | The operation that sent the message it completed |
5353
+
5354
+ Each is derived from what the row records: the publication it advanced, or the occurrence it materialized.
5355
+ A message names an invocation only to the worker whose operation sent it; history a FORK inherited names the
5356
+ same invocation as its original, at the same coordinate. The row's own `source` keeps its attribution meaning
5357
+ ({§env-delta-attribution}). An obligation whose opener was not recorded, and every other row, omits the field.
5358
+
5359
+ §methods-answering **A reply names the operation that sent the message it answers.** `readLog` and `log/entry`
5360
+ project `answering` on a reply delivered to the worker whose operation sent the message ({§message-reply-delivery}):
5361
+ that operation's log address, whether the reply completes the message or reports progress. A completion reply
5362
+ names the same address as its `invocation`. A reply to a message this worker did not send, such as another actor
5363
+ answering a message in this worker's inbox, omits the field, as does every other row.
5198
5364
 
5199
5365
  ### §notifications Core events
5200
5366
 
@@ -5202,13 +5368,13 @@ adding a loop to it. LOOK text anchors resolve through the same
5202
5368
  |--------------------------------------------------------------|---------|------------|
5203
5369
  | §notifications-operation-event `operation/event` | `ApplicationOperationEvent`: `{ workerId, loopId, turnId, sequence, origin, projectRoot, statement, phase, result? }` | One admitted dispatch starts before capability/proposal admission and settles after its durable receipt(s). Only the settled phase has `result`, the exact returned operation result. Resource fan-out is one dispatch ({§bulk-resource-targeting}); BARE starts before prompt preparation and settles after ordered receipt persistence. Automatic stream observations and log writes are not dispatches. An asynchronous executor's successful dispatch does not assert process exit. An internal exception that prevents settlement has no invented result event. |
5204
5370
  | §notifications-log-entry-notify `log/entry` | `{ entry: LogEntry }` | A non-proposed `log_entries` row is committed, or a proposed row reaches terminal settlement under {§proposal-proposed-hidden}. Delivery completes before a later event may terminate the owning Loop. |
5205
- | §notifications-loop-terminated `loop/terminated` | `{ workerId, loopId, result, hitMaxTurns, turnIds, usage: { accounting, curationWeight, curationBudget, contextTokens, contextCapacity, meta }, attributions }` | One loop reaches a terminal state. `result` is the exact universal operation result, including its RFC 9457 Problem Details on failure. `accounting` is the loop's contracts-owned {§provider-accounting}; the two curation facts and two physical-context facts follow {§tokenomics-client-gauge}; `meta` is that turn's opaque provider bag. `attributions` is the sorted union of exact provider-request evidence ({§attribution}), separate from accounting. Worker and loop are an inseparable owning coordinate. |
5371
+ | §notifications-loop-terminated `loop/terminated` | `{ workerId, loopId, result, hitMaxTurns, turnIds, usage: { accounting, curationWeight, curationBudget, contextTokens, contextCapacity, meta }, attributions, terminatedAt }` | One loop reaches a terminal state. `terminatedAt` is the loop's own stamp in Unix milliseconds, the one a projection reads ({§application-loop-observation}). `result` is the exact universal operation result, including its RFC 9457 Problem Details on failure. `accounting` is the loop's contracts-owned {§provider-accounting}; the two curation facts and two physical-context facts follow {§tokenomics-client-gauge}; `meta` is that turn's opaque provider bag. `attributions` is the sorted union of exact provider-request evidence ({§attribution}), separate from accounting. Worker and loop are an inseparable owning coordinate. |
5206
5372
  | §notifications-loop-packet `loop/packet` | `{ workerId, loopId, packetCount }` | One provider packet becomes durable. `packetCount` is the exact count of packet-bearing turns in that Loop; packetless producer turns and physical provider retries never contribute. |
5207
5373
  | §notifications-loop-proposal `loop/proposal` | contracts-owned `ProposalProjection` | Dispatch pauses on a durable 202 proposal. `disposition` is the sole authority for whether a client presents review UI; live and reconnect share {§proposal-projection}. |
5208
5374
  | §notifications-loop-interaction `loop/interaction` | contracts-owned `ClientInteractionProjection` | An operation is paused on client input. Live delivery and reconnect discovery share {§client-interactions}; workspace scope remains the event envelope. |
5209
5375
  | §notifications-workspace-created `workspace/created` | `{ id, name, projectRoot }` | A workspace is created. This is the only current global event. |
5210
5376
  | `workspace/preparation` | `{ workspaceId, preparation: FunctionalityPreparationActivity[] }` | Workspace capability preparation changes; snapshot and clearing semantics follow {§functionality-preparation-visibility}. |
5211
- | §notifications-stream-event-on-channel-change `stream/event` | `{ entryId, workerId, target, channel, state, contentLength, mimetype?, loop_seq?, turn_seq?, sequence? }` | Channel content grows or channel state transitions. `workerId` is the initiating actor used for conversation routing, never entry ownership or access control. `target` is the canonical resource URI. Optional numeric coordinates identify the causal log item, independently of that URI. Core-managed channel writes include the current stored `mimetype`, which may change per call ({§channel-mimetype}); the generic extension notification capability does not require it. It carries metadata, not content; consumers read bytes by canonical workspace address. |
5377
+ | §notifications-stream-event-on-channel-change `stream/event` | `{ entryId, workerId, target, channel, state, contentLength, mimetype?, loop_seq?, turn_seq?, sequence? }` | Channel content grows or channel state transitions. `workerId` is the initiating actor used for routing to the bound worker, never entry ownership or access control. `target` is the canonical resource URI. Optional numeric coordinates identify the causal log item, independently of that URI. Core-managed channel writes include the current stored `mimetype`, which may change per call ({§channel-mimetype}); the generic extension notification capability does not require it. It carries metadata, not content; consumers read bytes by canonical workspace address. |
5212
5378
  | §notifications-stream-concluded `stream/concluded` | `{ entryId, workerId, target, subscriptionId, scheme, result, summary, wakeAction, loop_seq?, turn_seq?, sequence? }` | A subscription closes. `workerId` identifies the initiating actor; `target` is the canonical resource URI. Optional numeric fields identify the causal log item, never parsed from `target`. Exact result truth is preserved. `wakeAction` reports `wake-pending` before settlement, `no-op-active-loop` when work is already executing, `no-loop`, or `skipped-aborted`/`skipped-cancelled` for an aborted worker scope. A pending wake predicts neither execution nor recipient count; subsequent ordinary loop events report actual progress and completion. |
5213
5379
  | §notifications-notice-event `notice/event` | `{ workerId, loopId, notice: Notice }` | A transient observation or progress notice occurs. `workerId` owns loop activity; only workspace derivation progress uses `null` with `loopId=0`. It cannot alter durable history, scheduling, recovery, or model-visible failure truth. |
5214
5380
  | §notifications-outside-event `outside/event` | `{ workerId, loopId, turnId, coordinate, text, tokens }` | An admitted emission carried text outside every operation ({§outside-text}): once per admitted emission, the exact stored text, its packet weight, and the turn's `<worker>-<loop>-<turn>` coordinate. It is transient presentation evidence; the turn's `outside` source remains the durable authority. |
@@ -5231,21 +5397,66 @@ the authoritative scope and is `null` only for a global event. Core does not
5231
5397
  mutate each payload to repeat it. A transport module stamps that scope onto any
5232
5398
  outward envelope that requires it and owns workspace fan-out.
5233
5399
 
5234
- ### §connection-lifecycle Client action evidence
5400
+ ### §worker-external-reference External worker references
5401
+
5402
+ An adapter's `(namespace, reference)` identifies one durable model worker. Both strings are opaque
5403
+ to core; protocol syntax belongs to the adapter. `readWorkerReference` returns its workspace and
5404
+ worker IDs or null, without creating anything. `ensureWorkerReference` returns the same identity,
5405
+ atomically creating an ordinary folderless workspace and root model worker if absent. Names follow
5406
+ {§workspace-auto-name} and {§worker-auto-name}; no owner is claimed and no work starts.
5407
+
5408
+ The association is immutable while its worker exists. Concurrent allocation creates exactly one
5409
+ workspace, worker and association; failure leaves none. Renaming a workspace, changing approval
5410
+ ownership, reconnecting or restarting does not change the association. Worker deletion removes it
5411
+ through its foreign key. Namespaces isolate adapter identities, not authorization: the daemon
5412
+ remains one trust domain. `listWorkerReferences` inspects one namespace's references to a worker,
5413
+ bounded by its workspace. New workspace creation emits the ordinary `workspace/created` event.
5235
5414
 
5236
- A module client is an actor ({§machine-processes}). Its dispatched side effects
5237
- write to its own client worker with `origin="client"` and execute in the
5238
- workspace's Functionality
5239
- ({§actor-boundary-attached-functionality}); one client action owns
5240
- one administrative loop, and its statements become ordered operation turns
5241
- inside that loop. A proposal may hold its turn and loop across an external
5242
- interrupt/resume, but those records preserve durable evidence rather than
5243
- defining the public client lifecycle. Multiple client actors have distinct workers.
5415
+ ### §connection-lifecycle Client operations
5244
5416
 
5245
- `runLoop` targets a separate model worker holding the conversation with
5246
- `origin="model"`. Both workers share workspace state, while a packet renders
5247
- only the model worker's private log; client action rows are structurally absent
5248
- without an origin filter ({§actor-boundary-isolation}).
5417
+ A worker is a model's identity. A person's client operations (an execution, typed
5418
+ statements, a rollback's compensations) are the workspace's, so each journals in the
5419
+ workspace's runtime worker `_plurnk`, created when absent, and executes in the workspace's
5420
+ Functionality ({§actor-boundary-attached-functionality}). Binding a workspace mints nothing.
5421
+
5422
+ | | A model worker | A client operation in `_plurnk` |
5423
+ |---|---|---|
5424
+ | Who acted | the worker | the person its loop names ({§actor-boundary-client-operations}) |
5425
+ | Who answers its proposals | the worker's owner reviews them | nobody: it is that person's decision already ({§proposal-disposition}) |
5426
+ | Who answers its clarifications | its owner, while it can ({§client-interaction-routing}) | nobody: the runtime's owner receives no client tool, so an interaction the operation needs fails at once as unsupported |
5427
+ | What is cancelled and observed together | the worker's loops | the operation's own administrative loop |
5428
+ | What its `env:///` means | its own layer, above the workspace's | the workspace's layer; `_plurnk` keeps none ({§env-scheme}) |
5429
+ | Whose voice its messages carry | its own | the person's, as a thread message's is |
5430
+ | Whose log its log addresses and FORK mean | its own | the worker the person acts for: read and branched, never curated |
5431
+ | What its WORK starts | its own child | a root worker the person owns |
5432
+
5433
+ One client operation owns one administrative loop, and its statements become ordered
5434
+ `client` operation turns inside it. The loop carries its own cancellation scope, and
5435
+ nothing else in `_plurnk` (another person's operation, the daemon's narration, a hosted A2A
5436
+ task) shares it. `cancelLoop` cancels that operation alone: a dispatch still running
5437
+ ends `operation-cancelled`, as does its loop, and every attached execution its rows started
5438
+ that still runs is reaped ({§worker-lifecycle-total-reap}), its stream concluding aborted even
5439
+ after the loop settled. A detached execution is nobody's obligation, and `cancelLoop` leaves it
5440
+ running. Shutdown cancels every operation the same way.
5441
+
5442
+ `runLoop` targets a model worker with `origin="model"`. A packet renders only that
5443
+ worker's private log, so a client operation's rows are structurally absent from every
5444
+ model's packet ({§actor-boundary-isolation}); a person reaches a model through a message.
5445
+
5446
+ §actor-boundary-client-operations **A client operation acts on workspace resources, attributes messages
5447
+ to its person, and reads a log in the context of one worker.** Its loop's `promptSource`
5448
+ ({§methods-worker-loops}) is the acting person's canonical address, the form arrivals use
5449
+ ({§message-causal-source}); the daemon's own loops in `_plurnk` have none. Its rows are acts, not arrivals:
5450
+ they keep writer `origin: "client"`, so a person never writes as the runtime
5451
+ ({§scheme-surface-writableby-403}), and `source` null, so a person's SEND replies and completes as any
5452
+ author's does. Acting for no worker, an operation names no log: `log:///` is 404 `log-unaddressed` and
5453
+ FORK is 404 `fork-unaddressed`.
5454
+
5455
+ | Frame | Covers | So |
5456
+ |---|---|---|
5457
+ | Workspace resources | files, executions, Functionality, `env:///` | `_plurnk` keeps no layer of its own, so a person's `env:///` reads and writes the workspace's ({§env-scheme}) |
5458
+ | The person's voice | messages and delegated prompts | a message the operation delivers (SEND, or WORK to a worker that exists) arrives from the person, as a thread message does; WORK to a new worker starts a root model worker the person owns, its first prompt from them |
5459
+ | The worker it acts for | `log:///`, and the log FORK branches | READ and FIND answer from that worker's log as it reads it; FORK branches it as `forkWorker` does, its task from the person; curating the log is a client write it refuses ({§scheme-surface-writableby-403}) |
5249
5460
 
5250
5461
  ---
5251
5462
 
@@ -5319,7 +5530,7 @@ Conditional absence never reorders the surviving default sections.
5319
5530
  | 6 | user | `git` | Per-turn workspace status; empty content is omitted. |
5320
5531
  | 7 | user | `budget` | `Context Curation`; omitted when capacity is unknown. |
5321
5532
  | 8 | user | `worker` | `Worker`: `{"path": "worker://alice", "parent": <address or null>, "loop": L, "turn": T}`, the actor and the coordinate this packet's response becomes ({§packet-current-turn}). |
5322
- | 9 | user | `delegation` | `Delegation`: per-turn `{workers, streams}` pointers; always present, each list `[]` when empty ({§packet-empty-sections}). |
5533
+ | 9 | user | `work` | `Open Work`: per-turn `{workers, streams, messages}` pointers; always present, each list `[]` when empty ({§packet-empty-sections}). |
5323
5534
  | 10 | user | `messages` | `Open Messages`: immutable message addresses and causal sources ({§message-arrival}). |
5324
5535
  | 11 | user | `notices` | Per-turn observations; empty content is omitted. |
5325
5536
  | 12 | user | `errors` | Per-turn failure pointers; empty content is omitted. |
@@ -5329,7 +5540,7 @@ The order favors prefix-cache locality where semantics permit: the definition
5329
5540
  and privileged policy lead operator notes, while the append-mostly
5330
5541
  log and interpolated programs lead the volatile user-status clump. That clump is never cached
5331
5542
  across turns, since the next turn's log grows into its place, so its order serves attention alone:
5332
- it runs from ambient state (Git Status, Context, Worker, Delegation) to what the turn owes (Open
5543
+ it runs from ambient state (Git Status, Context, Worker, Open Work) to what the turn owes (Open
5333
5544
  Messages) to feedback on the last turn (Notices, then Errors), and closes with the Recap. The nearer
5334
5545
  the end, the more a section asks of this turn. With `all`, appending new
5335
5546
  observations and programs preserves the existing history's roles and text prefix; no old program
@@ -5454,7 +5665,7 @@ flowchart LR
5454
5665
  log --> packet["Packet lists coordinate;<br/>READ recalls exact body"]
5455
5666
  ```
5456
5667
 
5457
- §env-delta-log-pull **Pull the event record, never a world snapshot.** At
5668
+ §env-delta-log-pull **Pull the event record, never a workspace snapshot.** At
5458
5669
  pre-turn, a worker materializes only occurrences whose structural audience
5459
5670
  includes that worker. The set is exhaustive, unranked, and exactly once; the
5460
5671
  engine makes no relevance decision. Each copied event retains the operation,
@@ -5648,9 +5859,9 @@ another admission is a 409 conflict, not a second message or an implicit content
5648
5859
 
5649
5860
  | Audience | Delivery | Effect |
5650
5861
  |---|---|---|
5651
- | Assigned worker | Its conversation, even when another actor answered | Visible reply; wakes eligible parked work without a new Open Message or loop. |
5862
+ | Assigned worker | Its own log, even when another actor answered | Visible reply; wakes eligible parked work without a new Open Message or loop. |
5652
5863
  | Original native sender | That worker, if distinct from the assigned worker; original delegated-task answers use {§worker-scheme-collect} instead | Other replies use the same wake and observation path. |
5653
- | Exterior sender | The assigned conversation's protocol adapter | The adapter delivers the answer through its standard message channel. |
5864
+ | Exterior sender | The assigned worker's protocol adapter | The adapter delivers the answer through its standard message channel. |
5654
5865
  | Replying actor | Its own executed SEND | No duplicate ambient occurrence. |
5655
5866
 
5656
5867
  The successful SEND and its addressed occurrences commit together. Reply occurrences use
@@ -5681,7 +5892,9 @@ published, oldest first, and stamps each with the row it became. Ordinal 1 is th
5681
5892
  own assignment and shares the loop's fate: a loop that fails before its first turn does
5682
5893
  not replay it. Every other still-unpublished message at conclusion moves into one
5683
5894
  source-keyed recovery loop, renumbered from its first, whose headline it becomes; that loop's first turn publishes the complete
5684
- ordered set exactly once. Recovery retries complete the same queued loop and never
5895
+ ordered set exactly once. The recovery loop retains the source loop's model routes,
5896
+ effort, and call ceiling; message bodies and attribution are preserved directly from
5897
+ their durable rows. Recovery retries complete the same queued loop and never
5685
5898
  mint duplicate work. Output withholding preserves readable arrival rows; explicit
5686
5899
  KILL follows the ordinary log contract.
5687
5900
 
@@ -5747,9 +5960,9 @@ retain distinct contracts and lifetimes.
5747
5960
  | `parse_advisory` | grammar parser — recoverable near-miss which did not invalidate the parsed statements | YOU SHOULD write the canonical form. | content-offset into the model's emission |
5748
5961
  | `search_progress` | repository materialization/indexing lifecycle ({§persistent-search-index}); structured phase, count, and percent; `level: info`, `warn` when a completed pass carries failed members ({§derivation-member-failure}), `error` on terminal failure | none | none |
5749
5962
  | `git_inspection_refused` | engine membership — automatic Git inspection refused a supplied repository whose config declares a `filter.*` program ({§membership-git-hermetic}); names the key; `level: warn`, once per workspace until the key changes or clears | none | none |
5750
- | `continued_without_note` | engine turn — a continuing or parked model turn that admitted no NOTE ({§continued-without-note}); `level: warn` | YOU MUST record at least one NOTE along with other operations. | none |
5963
+ | `continued_without_note` | engine turn — a continuing or parked model turn that admitted no NOTE ({§continued-without-note}); `level: warn` | YOU MUST emit at least one NOTE in every turn. | none |
5751
5964
  | `outside_text` | engine turn — text the emission carried outside its operations, discarded ({§outside-text}); `level: warn` | YOU MUST NOT emit anything except whitespace between fenced operations. | none |
5752
- | `invalid_emission` | engine grammar — a response rejected before dispatch; `level: error` | YOU MUST ONLY emit fenced operations; with the parameters, pattern, and terse aside on the fenced Operation line. | content-offset of the parser diagnostic, when it has one |
5965
+ | `invalid_emission` | engine grammar — a response rejected before dispatch; `level: error` | YOU MUST ONLY emit fenced operations; with the parameters, pattern, and terse aside on the fenced operation line. | content-offset of the parser diagnostic, when it has one |
5753
5966
  | `output_truncated` | engine capacity or turn — an emission cut at the output allowance ({§output-allowance-notice}); `level: error` when the cut response was rejected, otherwise `warn` | YOU SHOULD emit fewer or shorter operations per turn. | none |
5754
5967
  | `provider_warning` | a provider warning: an SDK compatibility note, or native tool calls in a response that declared none; `level: warn` | YOU MUST NOT emit native tool calls. on the native tool calls; none on a compatibility note | none |
5755
5968
  | `provider_retry` | a provider stream that failed after output began and was answered by a complete retry; `level: warn` | none | none |
@@ -5761,13 +5974,13 @@ retain distinct contracts and lifetimes.
5761
5974
  | `scope_normalized` | engine slicer — a scope normalized to its canonical form; `level: warn` | YOU SHOULD write the normalized scope. | none |
5762
5975
  | `edit_merged` | engine slicer — an EDIT resolution rule applied to a batch ({§edit-batch-merges}); `level: warn` | YOU SHOULD verify the merged EDIT before building on it. | none |
5763
5976
 
5764
- §continued-without-note **A continuing turn that lapsed from the per-turn rule is told so.** The card's rule is one line, `YOU MUST record at least one NOTE along with other operations.`, and the daemon's directive quotes it. When a model turn settles at 102 or 202, so the loop continues past it or parks on it, and it admitted no NOTE in its program or from its reasoning ({§reasoning-operations}), the next packet carries one `engine:turn` notice, `continued_without_note`, `level: warn`, with that directive and the message `The turn continued without a NOTE.`; when its program was NOTEs and nothing else, the next packet carries `noted_without_operation`, the same directive and the message `The turn carried NOTEs and no other operation.` Each is a notice, never a failure: it mints no row, never counts toward the strike rail, and never ends or alters a loop. A turn that concludes its loop, an empty turn, which carries its own no-operation row, and every non-model turn take none.
5977
+ §continued-without-note **A continuing turn that carried no NOTE is told so.** The card teaches NOTE as the register of every plan, finding, decision and next action; the notice's directive is the command `YOU MUST emit at least one NOTE in every turn.` When a model turn settles at 102 or 202, so the loop continues past it or parks on it, and it admitted no NOTE in its program or from its reasoning ({§reasoning-operations}), the next packet carries one `engine:turn` notice, `continued_without_note`, `level: warn`, with that directive and the message `The turn continued without a NOTE.` It is a notice, never a failure: it mints no row, never counts toward the strike rail, and never ends or alters a loop. A turn that concludes its loop, an empty turn, which carries its own no-operation row, and every non-model turn take none.
5765
5978
 
5766
5979
  §notice-callout **A notice speaks in the card's callout form.** Each renders as one alert callout, its level choosing the callout: `> [!CAUTION]` for `error`, `> [!WARNING]` for `warn`, and `> [!NOTE]` for `info`, which no model packet carries. Inside it stand the directive ({§notice-directive}), then the message, any typed position as `@ line:column`, and the kind in brackets; a blank line separates consecutive callouts:
5767
5980
 
5768
5981
  ```text
5769
5982
  > [!WARNING]
5770
- > YOU MUST record at least one NOTE along with other operations. The turn continued without a NOTE. [continued_without_note]
5983
+ > YOU SHOULD write the normalized scope. Scope <0,120> was normalized to <1,120>. [scope_normalized]
5771
5984
  ```
5772
5985
 
5773
5986
  §notice-level **Severity on the wire (`level`, required).** Every `Notice` carries `level: "error" | "warn" | "info"`, set by the **producer** at the emit site. The level is client presentation, not operation status: even an `error` notice cannot terminalize work or substitute for a durable Problem. A forwarded `output_unaccounted` is `warn`; ordinary lifecycle and progress notices are `info`. Clients color straight off `level` without interpreting the open `kind` vocabulary.
@@ -5777,7 +5990,10 @@ retain distinct contracts and lifetimes.
5777
5990
  §notice-event-notify **Client surface.** Engine Notices broadcast live via the `notice/event` notification — `{ workerId, loopId, notice: { source, kind, level, message?, position?, …kind-specific } }` per the grammar's `Notice` schema — the moment they land. A loop Notice names its owning Worker; workspace derivation progress alone carries `workerId=null, loopId=0`. AG-UI projects the same observation as the custom `plurnk.notice` event. Failures do not broadcast on this surface: they are log rows, and the client reads them through `log.read` / the `log/entry` notification, the durable log.
5778
5991
 
5779
5992
  §loop-status-notice **The drain publishes lifecycle and its observation deadline together.**
5780
- `notice/event` carries `{ workerId, loopId, notice: { source: "engine:lifecycle", kind: "loop_status", level: "info", status, waitUntil } }`.
5993
+ `notice/event` carries `{ workerId, loopId, notice: { source: "engine:lifecycle", kind: "loop_status", level: "info", status, waitUntil, packetCount, claimedAt } }`:
5994
+ with its status, every beat carries its loop's packet count and claim stamp (Unix milliseconds, `null`
5995
+ before the first claim), the ones a projection reads ({§application-loop-observation}), so a client
5996
+ updates the loop's identity, count and stamp together.
5781
5997
 
5782
5998
  | Edge | `status` | `waitUntil` |
5783
5999
  |---|---|---|
@@ -5799,7 +6015,7 @@ the same deadline through {§application-loop-observation}, not a restarted cloc
5799
6015
 
5800
6016
  §candidate-pinned-runtime **Candidate runtime stays pinned.** At launch, after its optional build, the candidate copies every workspace's package projection (`files`) into `<state>/runtime`, links third-party dependencies, and resolves `@plurnk/*` to those copies. Its daemon and its digest export both run from that copy, never the shared checkout, so neither source edits nor a concurrent candidate's or developer's rebuild after launch changes the code a run finishes on. The copy is removed once the digest is written.
5801
6017
 
5802
- §output-allowance-notice **The output allowance is not disclosed; a ceiling cut names its cause.** The packet's budget section carries the curation state and no response allowance: a model does not plan in tokens and no harness tells it its output ceiling, so the number is a fact without a use (#826). Overflow tolerance (#482) is likewise never advertised; a cut's notice names the true per-call grant from the response's own capacity record. When a provider finish is `length`, the engine emits an `output_truncated` notice (source `engine:capacity`) naming the allowance — the fact alone, never advice on what to do about it — on every path — railed or not — and the rails verdict never blames the model's grammar for a cut the engine's own ceiling made. The same precedence governs a cut so deep no operation parses: the rejection notice names the truncation as the cause, not the parser's symptom, overriding {§invalid-emission-attempts}'s parser diagnostic for `length` finishes.
6018
+ §output-allowance-notice **The output allowance is not disclosed; a ceiling cut names its cause.** The packet's budget section carries the curation state and no response allowance: a model does not plan in tokens and no harness tells it its output ceiling, so the number is a fact without a use (#826). Overflow tolerance (#482) is likewise never advertised; a cut's notice names the true per-call grant from the response's own capacity record. When a provider finish is `length`, the engine emits an `output_truncated` notice (source `engine:capacity`) naming the allowance — the fact alone, never advice on what to do about it — on every path — railed or not — and the rails verdict never blames the model for a cut the engine's own ceiling made: a cut turn is no repetition evidence ({§engine-cycle-evidence}), and a strike threshold crossed on one names the allowance ({§engine-rails}). The same precedence governs a cut so deep no operation parses: the rejection notice names the truncation as the cause, not the parser's symptom, overriding {§invalid-emission-attempts}'s parser diagnostic for `length` finishes.
5803
6019
 
5804
6020
  §turn-lifecycle **Turn-lifecycle liveness.** Provider generation is the long, opaque window in a turn — one or more same-packet emission attempts may occur before the first committed op. A static client screen there is indistinguishable from a hang. The engine brackets the complete attempt window with two `notice/event` notices (`source: "engine:turn"`, `level: "info"`): `turn_awaiting_model` before the first call and `turn_generated` when an emission is accepted or the attempt budget is exhausted. The completion beat carries the spend ({§turn-accounting-notice}). Rejected content never rides the notice channel. Both are suppressed on an aborted loop and broadcast to the workspace like any notice ({§notice-event-notify}).
5805
6021
 
@@ -6389,7 +6605,7 @@ llama-server rails, the upstream A2A TCK and the client's real-model TUI tier ar
6389
6605
  run wherever their requirements hold. `tier:history` ledgers every census under the home: per
6390
6606
  specimen, its verdicts over runs and its last reason.
6391
6607
 
6392
- §fs-world-state **The world-state harness — coverage that closes the class.** Op-outcome tests check what an op returned; the harness checks the resulting world. `WorldState.check(db)` asserts, pure-db and read-only: identity uniqueness in practice (no tuple holds two rows), the canonical fixpoint on every file-class key, channel orphan-freedom, the closed admission set (every file row's origin is Git or constraint), and sig-coherence. Generated-pick incorporation and lifecycle require filesystem/Git evidence and are covered by the composed creation matrix rather than a false pure-database proxy. The harness runs as a lifecycle-test epilogue and at every soak turn boundary, where the delta half applies: an idle turn grows the entries table by ZERO. A violation names its law and its row.
6608
+ §fs-resource-state **The resource-state harness — coverage that closes the class.** Op-outcome tests check what an op returned; the harness checks the resulting resource state. `ResourceState.check(db)` asserts, pure-db and read-only: identity uniqueness in practice (no tuple holds two rows), the canonical fixpoint on every file-class key, channel orphan-freedom, the closed admission set (every file row's origin is Git or constraint), and sig-coherence. Generated-pick incorporation and lifecycle require filesystem/Git evidence and are covered by the composed creation matrix rather than a false pure-database proxy. The harness runs as a lifecycle-test epilogue and at every soak turn boundary, where the delta half applies: an idle turn grows the entries table by ZERO. A violation names its law and its row.
6393
6609
 
6394
6610
  ## Problem codes and pinned wording
6395
6611
 
@@ -6428,6 +6644,7 @@ Every Problem code core mints is named here under its family ({§problem-error-c
6428
6644
  | `channel-required` | 400 | The '*scheme*' scheme has no default channel. Recovery: Address a named channel with a URI fragment. |
6429
6645
  | `binary-source-unsupported` | 415 | Channel #*name* is binary and its scheme keeps no bytes to transfer. |
6430
6646
  | `metadata-unsupported` | 400 | *OP* takes only the env option; '*key*' is not one. A WORK to a held name with env: WORK to the existing worker '*name*' carries env; a message takes none. |
6647
+ | `fork-unaddressed` | 404 | This client operation acts for no worker, so FORK has no log to branch ({§actor-boundary-client-operations}). |
6431
6648
  | `worker-name-conflict` | 409 | Worker '*name*' already exists in this workspace. Recovery: WORK or SEND (worker://*name*) delivers a message to the live worker; FORK with a name no worker holds. ({§worker-scheme-spawn}) |
6432
6649
  | `no-operation` | 422 | The turn performed no operation ({§empty-turn}). |
6433
6650
  | `send-target-not-a-recipient` | 400 | The addressed scheme is not a SEND recipient. Recovery: Name a message address or a recipient that implements SEND. |
@@ -6491,7 +6708,7 @@ lists the schemes the workspace registers, and rebuilds the address into nothing
6491
6708
  | `file-create-excluded` | 403 | A members exclusion (`!_glob_`) covers '*path*'. Recovery: Remove or disable the excluding members definition, or choose another path. |
6492
6709
  | `file-create-gitignored` | 403 | Active Git policy ignores '*path*', and no members definition includes it. Recovery: Choose a Git-admitted path or add a members definition that includes it. |
6493
6710
  | `file-materialization-limit` | 413 | The file exceeds the materialization byte limit and is not read into the workspace. |
6494
- | `entry-not-found` | 404 | No member of this workspace is at '*path*'. Recovery: FIND lists paths. EDIT creates files; `members (add)` admits existing files with a `{"alias": "<name>", "definition": {"glob": "<path>"}}` body. A path beyond the root recovers instead to: '*path*' is outside the project root: only a members definition under the operator's namespace scope admits it. |
6711
+ | `entry-not-found` | 404 | No member of this workspace is at '*path*'. Recovery: FIND lists paths. EDIT creates files; `members (add)` admits existing files with a `{"files": ["<path>"]}` body. A path beyond the root recovers instead to: '*path*' is outside the project root: only a members definition under the operator's namespace scope admits it. |
6495
6712
  | `body-channel-required` | 400 | A file write requires a body channel. |
6496
6713
  | `write-failed` | 500 | Publication or its settlement failed; the result distinguishes an unpublished write from an already-published one. |
6497
6714
  | `creation-rollback-failed` | 500 | Incorporating a newly created file failed, and rollback was incomplete. |
@@ -6527,6 +6744,7 @@ lists the schemes the workspace registers, and rebuilds the address into nothing
6527
6744
  |---|---:|---|
6528
6745
  | `read-target-required` | 400 | READ requires a log coordinate. Recovery: Provide one exact log coordinate. |
6529
6746
  | `coordinate-malformed` | 400 | The log coordinate '*path*' is malformed. Recovery: Use one exact loop/turn/sequence coordinate. |
6747
+ | `log-unaddressed` | 404 | This client operation acts for no worker, so `log:///` names no log ({§actor-boundary-client-operations}). |
6530
6748
  | `kill-failed` | 400 | The log target '*path*' is malformed. (A glob the matcher cannot read: The log glob '*glob*' is malformed.) Recovery: the {§log-target-shape} sentence. |
6531
6749
  | `curation-failed` | 400 | A scoped KILL's selection could not be read: the same sentences and recovery as `kill-failed`. |
6532
6750
  | `worker-target-required` | 400 | EDIT requires a worker:// target. Recovery: Provide the worker target. |
@@ -6547,14 +6765,16 @@ lists the schemes the workspace registers, and rebuilds the address into nothing
6547
6765
  | code | status | condition |
6548
6766
  |---|---:|---|
6549
6767
  | `interaction-not-pending` | 409 | The requested client interaction is not pending. |
6550
- | `interaction-unsupported` | 501 | No recipient implements the requested tool for this conversation. |
6768
+ | `interaction-unsupported` | 501 | No recipient implements the requested tool for this worker. |
6551
6769
  | `recipient-mismatch` | 403 | Only the interaction's recipient may answer it. |
6552
6770
  | `interaction-response-invalid` | 400 | The response does not satisfy the pending interaction's schema. |
6553
6771
  | `invalid-body` | 400 | A question body is not JSON, or lacks its nonempty message or requested schema. |
6554
6772
  | `offset-invalid` | 400 | An entry offset is not a nonnegative safe integer. |
6555
6773
  | `userinfo-not-allowed` | 400 | An entry target URL contains userinfo. |
6556
6774
  | `action-threw` | 500 | The client action failed outside its operation result contract. |
6775
+ | `operation-cancelled` | 499 | The client operation was cancelled: *reason*. Its loop settles with this result ({§connection-lifecycle}). |
6557
6776
  | `message-already-accepted` | 409 | The message address was already accepted in this workspace. |
6777
+ | `message-address-reused` | 422 | The message address was already accepted in this workspace with other words. |
6558
6778
  | `source-not-running` | 409 | The originating task is not running; no message was admitted. |
6559
6779
  | `loop-not-open` | 409 | The named loop is not an unfinished recipient of the worker; no message was admitted. |
6560
6780
 
@@ -6601,17 +6821,18 @@ lists the schemes the workspace registers, and rebuilds the address into nothing
6601
6821
  | Nothing is in flight. Continuing. | a WAIT (or a premature terminal) with no live work ({§wait-obligation-matrix}) |
6602
6822
  | All Open Messages resolved. Review new results; emit no OPs if finished. | resolved messages with unobserved results ({§loop-completion}) |
6603
6823
  | Context exceeds budget. YOU MUST ONLY KILL, MOVE or NOTE this turn. | the over-budget row ({§context-over-budget-row}) |
6604
- | YOU MUST record at least one NOTE along with other operations. The turn continued without a NOTE. | a continuing or parked model turn without a NOTE ({§continued-without-note}) |
6824
+ | YOU MUST emit at least one NOTE in every turn. The turn continued without a NOTE. | a continuing or parked model turn without a NOTE ({§continued-without-note}) |
6605
6825
  | Each directive in the notice table | a notice of the model's own output ({§notice-directive}) |
6606
6826
  | Context window overflow: the packet cannot fit the model's window even as receipts. | the wall ({§context-wall}) |
6607
6827
  | No recipient implements the requested interaction. | The worker's owner is not interactive or does not declare the required client tool ({§client-interaction-routing}). |
6608
6828
  | Nobody is present to answer. Decide from what you already have, or conclude stating what you could not resolve. | dispatch of an interaction tool the worker's owner would not receive ({§owner-interaction-ring}) |
6609
6829
  | Worker name '*name*' must match `[A-Za-z0-9][A-Za-z0-9_-]{0,62}`. Recovery: Use 1–63 ASCII letters, digits, '_' or '-', starting with a letter or digit. | an invalid worker name |
6610
6830
  | Provide the client identifier. / Provide an absolute project path. / Use a positive integer limit. / prompt is not a non-empty string. | client input validation on the daemon's methods |
6831
+ | name '*name*' has the path segment '*segment*'. Recovery: Provide a name with no '.' or '..' between its slashes; a dot within a segment is ordinary. / name is not well-formed Unicode: it holds an unpaired surrogate. | a workspace name refused at creation or rename ({§workspace-name}) |
6611
6832
  | The stream was cancelled by KILL. | a stream terminal after KILL |
6612
6833
  | '*runtime*' exited with code 127; `*program*` is a registered tool of `*owner*`. | a tool run as a shell command ({§exec-tool-fall-through}) |
6613
6834
  | '*path*' is a directory, not a file; READ reads one file. Recovery: `FIND (_path_/)` lists its files. | READ of a directory |
6614
6835
  | The execution at ops://*worker*/*loop* has not concluded. | a bare READ of a running worker's result (425) |
6615
6836
  | The child provider failed. | a child's provider failure read back by its parent |
6616
- | '*path*' exists on disk but is not a member of this workspace. Recovery: Admit it with `members (add)` and a `{"alias": "<name>", "definition": {"glob": "<path>"}}` body. | a non-member on disk at the addressed path |
6837
+ | '*path*' exists on disk but is not a member of this workspace. Recovery: Admit it with `members (add)` and a `{"files": ["<path>"]}` body. | a non-member on disk at the addressed path |
6617
6838
  | '*path*' exists on disk but is not a member of this workspace. Recovery: The repository ignores it: a client or operator members definition can include it, a model definition cannot. | a non-member the repository ignores ({§membership-read-refusal}) |