@plurnk/plurnk-service 3.2.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 (328) hide show
  1. package/.env.defaults +3 -3
  2. package/INSTALL.md +6 -0
  3. package/README.md +2 -2
  4. package/SPEC.md +489 -186
  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/BudgetReadout.d.ts +1 -3
  21. package/dist/core/BudgetReadout.d.ts.map +1 -1
  22. package/dist/core/BudgetReadout.js +43 -46
  23. package/dist/core/BudgetReadout.js.map +1 -1
  24. package/dist/core/ChannelWrite.d.ts +10 -0
  25. package/dist/core/ChannelWrite.d.ts.map +1 -1
  26. package/dist/core/ChannelWrite.js +10 -0
  27. package/dist/core/ChannelWrite.js.map +1 -1
  28. package/dist/core/ChannelWrite.sql +14 -2
  29. package/dist/core/ClientInteractions.d.ts.map +1 -1
  30. package/dist/core/ClientInteractions.js +2 -1
  31. package/dist/core/ClientInteractions.js.map +1 -1
  32. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  33. package/dist/core/CoreSchemeServices.js +6 -0
  34. package/dist/core/CoreSchemeServices.js.map +1 -1
  35. package/dist/core/DataStatementRunner.js +4 -6
  36. package/dist/core/DataStatementRunner.js.map +1 -1
  37. package/dist/core/Dispatcher.d.ts +3 -7
  38. package/dist/core/Dispatcher.d.ts.map +1 -1
  39. package/dist/core/Dispatcher.js +53 -73
  40. package/dist/core/Dispatcher.js.map +1 -1
  41. package/dist/core/Dispatcher.sql +2 -2
  42. package/dist/core/EditMutations.js +1 -0
  43. package/dist/core/EditMutations.js.map +1 -1
  44. package/dist/core/Engine.d.ts +8 -14
  45. package/dist/core/Engine.d.ts.map +1 -1
  46. package/dist/core/Engine.js +22 -24
  47. package/dist/core/Engine.js.map +1 -1
  48. package/dist/core/Engine.sql +6 -6
  49. package/dist/core/ExecutorRegistry.d.ts +0 -5
  50. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  51. package/dist/core/ExecutorRegistry.js +10 -18
  52. package/dist/core/ExecutorRegistry.js.map +1 -1
  53. package/dist/core/FileAddress.js +1 -1
  54. package/dist/core/FileAddress.js.map +1 -1
  55. package/dist/core/FilePublication.js +10 -74
  56. package/dist/core/FilePublication.js.map +1 -1
  57. package/dist/core/FileSnapshot.js +36 -100
  58. package/dist/core/FileSnapshot.js.map +1 -1
  59. package/dist/core/HostPaths.d.ts +1 -0
  60. package/dist/core/HostPaths.d.ts.map +1 -1
  61. package/dist/core/HostPaths.js +11 -1
  62. package/dist/core/HostPaths.js.map +1 -1
  63. package/dist/core/KillHandler.js +3 -1
  64. package/dist/core/KillHandler.js.map +1 -1
  65. package/dist/core/LiveSubscription.js +1 -1
  66. package/dist/core/LiveSubscription.js.map +1 -1
  67. package/dist/core/LogBody.js +1 -1
  68. package/dist/core/LogBody.js.map +1 -1
  69. package/dist/core/LogWriter.js +1 -1
  70. package/dist/core/LogWriter.js.map +1 -1
  71. package/dist/core/LoopDriver.d.ts +3 -3
  72. package/dist/core/LoopDriver.d.ts.map +1 -1
  73. package/dist/core/LoopDriver.js +15 -13
  74. package/dist/core/LoopDriver.js.map +1 -1
  75. package/dist/core/LoopLifecycle.d.ts +5 -0
  76. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  77. package/dist/core/LoopLifecycle.js +14 -1
  78. package/dist/core/LoopLifecycle.js.map +1 -1
  79. package/dist/core/LoopLifecycle.sql +11 -2
  80. package/dist/core/MembershipMaterialization.js +2 -2
  81. package/dist/core/MembershipMaterialization.js.map +1 -1
  82. package/dist/core/MessageResources.d.ts +3 -0
  83. package/dist/core/MessageResources.d.ts.map +1 -1
  84. package/dist/core/MessageResources.js +15 -1
  85. package/dist/core/MessageResources.js.map +1 -1
  86. package/dist/core/OperatorConfig.d.ts +1 -2
  87. package/dist/core/OperatorConfig.d.ts.map +1 -1
  88. package/dist/core/OperatorConfig.js +26 -92
  89. package/dist/core/OperatorConfig.js.map +1 -1
  90. package/dist/core/PacketBuilder.d.ts.map +1 -1
  91. package/dist/core/PacketBuilder.js +21 -18
  92. package/dist/core/PacketBuilder.js.map +1 -1
  93. package/dist/core/PacketBuilder.sql +18 -5
  94. package/dist/core/ProblemLog.d.ts +1 -0
  95. package/dist/core/ProblemLog.d.ts.map +1 -1
  96. package/dist/core/ProblemLog.js +2 -2
  97. package/dist/core/ProblemLog.js.map +1 -1
  98. package/dist/core/ProposalLifecycle.d.ts +1 -0
  99. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  100. package/dist/core/ProposalLifecycle.js +14 -8
  101. package/dist/core/ProposalLifecycle.js.map +1 -1
  102. package/dist/core/ProposalPolicies.d.ts +4 -1
  103. package/dist/core/ProposalPolicies.d.ts.map +1 -1
  104. package/dist/core/ProposalPolicies.js +5 -2
  105. package/dist/core/ProposalPolicies.js.map +1 -1
  106. package/dist/core/ResourceMutations.js +2 -0
  107. package/dist/core/ResourceMutations.js.map +1 -1
  108. package/dist/core/ResourceSelector.js +1 -1
  109. package/dist/core/ResourceSelector.js.map +1 -1
  110. package/dist/core/SchemeRegistry.js +1 -1
  111. package/dist/core/SchemeRegistry.js.map +1 -1
  112. package/dist/core/ServiceHelp.js +1 -1
  113. package/dist/core/ServiceHelp.js.map +1 -1
  114. package/dist/core/ServiceTeardown.js +9 -12
  115. package/dist/core/ServiceTeardown.js.map +1 -1
  116. package/dist/core/StrikeRail.d.ts +2 -1
  117. package/dist/core/StrikeRail.d.ts.map +1 -1
  118. package/dist/core/StrikeRail.js +11 -6
  119. package/dist/core/StrikeRail.js.map +1 -1
  120. package/dist/core/TerminalResult.js +1 -1
  121. package/dist/core/TerminalResult.js.map +1 -1
  122. package/dist/core/TurnDispositionHandler.d.ts +1 -1
  123. package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
  124. package/dist/core/TurnDispositionHandler.js +8 -5
  125. package/dist/core/TurnDispositionHandler.js.map +1 -1
  126. package/dist/core/TurnMaterialization.js +6 -4
  127. package/dist/core/TurnMaterialization.js.map +1 -1
  128. package/dist/core/TurnMaterialization.sql +1 -1
  129. package/dist/core/TurnOps.js +1 -0
  130. package/dist/core/TurnOps.js.map +1 -1
  131. package/dist/core/TurnRunner.d.ts +4 -2
  132. package/dist/core/TurnRunner.d.ts.map +1 -1
  133. package/dist/core/TurnRunner.js +63 -51
  134. package/dist/core/TurnRunner.js.map +1 -1
  135. package/dist/core/WorkerControlHandler.d.ts +2 -0
  136. package/dist/core/WorkerControlHandler.d.ts.map +1 -1
  137. package/dist/core/WorkerControlHandler.js +84 -16
  138. package/dist/core/WorkerControlHandler.js.map +1 -1
  139. package/dist/core/WorkerName.d.ts +0 -4
  140. package/dist/core/WorkerName.d.ts.map +1 -1
  141. package/dist/core/WorkerName.js +5 -30
  142. package/dist/core/WorkerName.js.map +1 -1
  143. package/dist/core/WorkerName.sql +4 -20
  144. package/dist/core/WorkerOwners.d.ts +1 -0
  145. package/dist/core/WorkerOwners.d.ts.map +1 -1
  146. package/dist/core/WorkerOwners.js +6 -0
  147. package/dist/core/WorkerOwners.js.map +1 -1
  148. package/dist/core/WorkerOwners.sql +7 -0
  149. package/dist/core/WorkspaceGate.d.ts +3 -1
  150. package/dist/core/WorkspaceGate.d.ts.map +1 -1
  151. package/dist/core/WorkspaceGate.js +59 -10
  152. package/dist/core/WorkspaceGate.js.map +1 -1
  153. package/dist/core/caps/SchemeCtxImpl.d.ts +3 -0
  154. package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
  155. package/dist/core/caps/SchemeCtxImpl.js +14 -0
  156. package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
  157. package/dist/core/env-catalog.js +1 -1
  158. package/dist/core/env-catalog.js.map +1 -1
  159. package/dist/core/fork.js +1 -1
  160. package/dist/core/fork.js.map +1 -1
  161. package/dist/core/fork.sql +7 -6
  162. package/dist/core/mutation-types.d.ts +1 -0
  163. package/dist/core/mutation-types.d.ts.map +1 -1
  164. package/dist/core/mutation-types.js +4 -1
  165. package/dist/core/mutation-types.js.map +1 -1
  166. package/dist/core/namespace.js +1 -1
  167. package/dist/core/namespace.js.map +1 -1
  168. package/dist/core/packet-wire.d.ts +1 -1
  169. package/dist/core/packet-wire.d.ts.map +1 -1
  170. package/dist/core/packet-wire.js +24 -15
  171. package/dist/core/packet-wire.js.map +1 -1
  172. package/dist/core/plurnk-uri.d.ts +1 -1
  173. package/dist/core/plurnk-uri.d.ts.map +1 -1
  174. package/dist/core/plurnk-uri.js +1 -1
  175. package/dist/core/plurnk-uri.js.map +1 -1
  176. package/dist/core/scheme-types.d.ts +5 -0
  177. package/dist/core/scheme-types.d.ts.map +1 -1
  178. package/dist/core/turn-signals.d.ts +4 -0
  179. package/dist/core/turn-signals.d.ts.map +1 -1
  180. package/dist/core/turn-signals.js +1 -0
  181. package/dist/core/turn-signals.js.map +1 -1
  182. package/dist/core/workspace-settings.js +1 -1
  183. package/dist/core/workspace-settings.js.map +1 -1
  184. package/dist/evidence/EvidenceReader.js +5 -5
  185. package/dist/evidence/EvidenceReader.js.map +1 -1
  186. package/dist/man/plurnk-service.1 +27 -6
  187. package/dist/schemes/Env.d.ts +7 -3
  188. package/dist/schemes/Env.d.ts.map +1 -1
  189. package/dist/schemes/Env.js +12 -6
  190. package/dist/schemes/Env.js.map +1 -1
  191. package/dist/schemes/Exec.d.ts.map +1 -1
  192. package/dist/schemes/Exec.js +37 -25
  193. package/dist/schemes/Exec.js.map +1 -1
  194. package/dist/schemes/ExecOutputScheme.js +3 -3
  195. package/dist/schemes/ExecOutputScheme.js.map +1 -1
  196. package/dist/schemes/ExecutionInput.d.ts +2 -1
  197. package/dist/schemes/ExecutionInput.d.ts.map +1 -1
  198. package/dist/schemes/ExecutionInput.js +45 -7
  199. package/dist/schemes/ExecutionInput.js.map +1 -1
  200. package/dist/schemes/File.d.ts.map +1 -1
  201. package/dist/schemes/File.js +32 -23
  202. package/dist/schemes/File.js.map +1 -1
  203. package/dist/schemes/Log.d.ts.map +1 -1
  204. package/dist/schemes/Log.js +23 -5
  205. package/dist/schemes/Log.js.map +1 -1
  206. package/dist/schemes/Log.sql +24 -1
  207. package/dist/schemes/ResourceTreeScheme.js +2 -5
  208. package/dist/schemes/ResourceTreeScheme.js.map +1 -1
  209. package/dist/schemes/TurnSource.js +1 -1
  210. package/dist/schemes/TurnSource.js.map +1 -1
  211. package/dist/schemes/Worker.d.ts.map +1 -1
  212. package/dist/schemes/Worker.js +10 -3
  213. package/dist/schemes/Worker.js.map +1 -1
  214. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  215. package/dist/schemes/_entry-crud.js +2 -4
  216. package/dist/schemes/_entry-crud.js.map +1 -1
  217. package/dist/schemes/_entry-crud.sql +5 -3
  218. package/dist/schemes/_entry-find.js +3 -3
  219. package/dist/schemes/_entry-find.js.map +1 -1
  220. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  221. package/dist/schemes/_entry-manifest.js +3 -3
  222. package/dist/schemes/_entry-manifest.js.map +1 -1
  223. package/dist/schemes/_entry-manifest.sql +2 -0
  224. package/dist/schemes/_entry-ops.js +1 -1
  225. package/dist/schemes/_entry-ops.js.map +1 -1
  226. package/dist/schemes/_entry-send.js +1 -1
  227. package/dist/schemes/_entry-send.js.map +1 -1
  228. package/dist/server/ClientReads.d.ts +1 -0
  229. package/dist/server/ClientReads.d.ts.map +1 -1
  230. package/dist/server/ClientReads.js +7 -2
  231. package/dist/server/ClientReads.js.map +1 -1
  232. package/dist/server/Credentials.d.ts +48 -0
  233. package/dist/server/Credentials.d.ts.map +1 -0
  234. package/dist/server/Credentials.js +203 -0
  235. package/dist/server/Credentials.js.map +1 -0
  236. package/dist/server/Daemon.d.ts +44 -39
  237. package/dist/server/Daemon.d.ts.map +1 -1
  238. package/dist/server/Daemon.js +287 -172
  239. package/dist/server/Daemon.js.map +1 -1
  240. package/dist/server/DrainSupervisor.d.ts +8 -3
  241. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  242. package/dist/server/DrainSupervisor.js +44 -32
  243. package/dist/server/DrainSupervisor.js.map +1 -1
  244. package/dist/server/EnvFunctionality.d.ts.map +1 -1
  245. package/dist/server/EnvFunctionality.js +2 -1
  246. package/dist/server/EnvFunctionality.js.map +1 -1
  247. package/dist/server/Functionality.d.ts +4 -2
  248. package/dist/server/Functionality.d.ts.map +1 -1
  249. package/dist/server/Functionality.js +192 -96
  250. package/dist/server/Functionality.js.map +1 -1
  251. package/dist/server/FunctionalityManager.d.ts +1 -4
  252. package/dist/server/FunctionalityManager.d.ts.map +1 -1
  253. package/dist/server/FunctionalityManager.js +6 -6
  254. package/dist/server/FunctionalityManager.js.map +1 -1
  255. package/dist/server/HttpListener.d.ts +1 -1
  256. package/dist/server/HttpListener.d.ts.map +1 -1
  257. package/dist/server/HttpListener.js.map +1 -1
  258. package/dist/server/MembersFunctionality.d.ts +7 -8
  259. package/dist/server/MembersFunctionality.d.ts.map +1 -1
  260. package/dist/server/MembersFunctionality.js +90 -56
  261. package/dist/server/MembersFunctionality.js.map +1 -1
  262. package/dist/server/MessageAbandonment.d.ts +7 -0
  263. package/dist/server/MessageAbandonment.d.ts.map +1 -0
  264. package/dist/server/MessageAbandonment.js +57 -0
  265. package/dist/server/MessageAbandonment.js.map +1 -0
  266. package/dist/server/ModuleHost.d.ts +2 -1
  267. package/dist/server/ModuleHost.d.ts.map +1 -1
  268. package/dist/server/PlurnkSkill.js +4 -7
  269. package/dist/server/PlurnkSkill.js.map +1 -1
  270. package/dist/server/Rollback.d.ts +29 -0
  271. package/dist/server/Rollback.d.ts.map +1 -0
  272. package/dist/server/Rollback.js +111 -0
  273. package/dist/server/Rollback.js.map +1 -0
  274. package/dist/server/WorkerModelResolver.js +1 -0
  275. package/dist/server/WorkerModelResolver.js.map +1 -1
  276. package/dist/server/WorkerReferences.d.ts +14 -0
  277. package/dist/server/WorkerReferences.d.ts.map +1 -0
  278. package/dist/server/WorkerReferences.js +24 -0
  279. package/dist/server/WorkerReferences.js.map +1 -0
  280. package/dist/server/WorkerReferences.sql +31 -0
  281. package/dist/server/WorkspaceCapabilities.d.ts +2 -0
  282. package/dist/server/WorkspaceCapabilities.d.ts.map +1 -1
  283. package/dist/server/WorkspaceCapabilities.js +35 -18
  284. package/dist/server/WorkspaceCapabilities.js.map +1 -1
  285. package/dist/server/WorkspacePlugins.js +1 -0
  286. package/dist/server/WorkspacePlugins.js.map +1 -1
  287. package/dist/server/WorkspaceResidency.d.ts +3 -1
  288. package/dist/server/WorkspaceResidency.d.ts.map +1 -1
  289. package/dist/server/WorkspaceResidency.js +12 -5
  290. package/dist/server/WorkspaceResidency.js.map +1 -1
  291. package/dist/server/client-input.d.ts +2 -0
  292. package/dist/server/client-input.d.ts.map +1 -1
  293. package/dist/server/client-input.js +28 -0
  294. package/dist/server/client-input.js.map +1 -1
  295. package/dist/server/drain.sql +46 -40
  296. package/dist/server/envelope.d.ts +5 -9
  297. package/dist/server/envelope.d.ts.map +1 -1
  298. package/dist/server/envelope.js +21 -84
  299. package/dist/server/envelope.js.map +1 -1
  300. package/dist/server/envelope.sql +17 -17
  301. package/dist/server/lifecycle-recovery.sql +5 -15
  302. package/dist/server/logEntry.d.ts.map +1 -1
  303. package/dist/server/logEntry.js +6 -0
  304. package/dist/server/logEntry.js.map +1 -1
  305. package/dist/server/logEntry.sql +94 -6
  306. package/dist/server/module-discovery.d.ts +1 -1
  307. package/dist/server/module-discovery.d.ts.map +1 -1
  308. package/dist/server/module-discovery.js +1 -0
  309. package/dist/server/module-discovery.js.map +1 -1
  310. package/dist/server/seam-loop.sql +15 -7
  311. package/dist/server/seam-proposal-list.sql +10 -4
  312. package/dist/service.d.ts.map +1 -1
  313. package/dist/service.js +7 -1
  314. package/dist/service.js.map +1 -1
  315. package/docs/members.md +20 -13
  316. package/migrations/002_workers.sql +2 -2
  317. package/migrations/006_log.sql +1 -1
  318. package/migrations/018_worker_owners.sql +1 -1
  319. package/migrations/026_message_obligations.sql +32 -0
  320. package/migrations/027_members_identity.sql +20 -0
  321. package/migrations/028_mutation_patch.sql +20 -0
  322. package/migrations/029_default_worker.sql +9 -0
  323. package/migrations/030_client_workers.sql +105 -0
  324. package/migrations/031_message_openers.sql +83 -0
  325. package/migrations/032_worker_references.sql +29 -0
  326. package/package.json +24 -24
  327. package/plurnk.nginx.conf +71 -0
  328. 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. An existing name returns 409 whose receipt names the working forms — `SEND (worker://<name>)` with the task as the body to give the live worker more work, or a name no worker holds for another; 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,11 +682,11 @@ 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": [...]}`, its unconcluded child
624
- workers and its open streams as `{status, path}` pointers (the same shape as
625
- the errors section). Body-suppressed child activity is durable
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
687
+ unconcluded child workers, its open streams and the messages it sent that await
688
+ completion ({§worker-obligations}) as `{status, path}` pointers (the same shape as
689
+ the errors section); a message pointer carries the recipient loop's status. Body-suppressed child activity is durable
626
690
  history; this section is the current inventory that keeps an active obligation
627
691
  visible even when no new activity arrived. Each open stream pointer carries
628
692
  elapsed runtime, output inactivity, and its channels' sizes and growth since
@@ -642,22 +706,24 @@ Every admitted authority is a literal `workers.name`; self-addressing uses the c
642
706
  Workspace schedules are not held work: an occurrence arrives as a message
643
707
  ({§schedule-delivery}).
644
708
  - §packet-empty-sections **Emptiness is stated where the model decides on it.**
645
- `## 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
646
710
  model decides whether to wait or complete on exactly these facts, so their
647
711
  emptiness is stated rather than inferred from a missing heading. This is the
648
712
  deliberate exception to the wire rule that empty content is absent; errors,
649
713
  notices, and the rest keep that rule. The parent is not a section: the
650
714
  `## Worker` identity block carries `"parent": "worker://<name>"`, or
651
715
  `"parent": null` at a root, so a worker never infers its rank from silence.
652
- - §packet-current-turn **The packet says who and which turn, below the log.** The
716
+ - §packet-current-turn **The packet says who, which turn and who attends, below the log.** The
653
717
  `## Worker` block, in the status clump below the log ({§packet-cache-monotone}), carries
654
- `{"path": "worker://<name>", "parent": <address or null>, "loop": L, "turn": T}`: the actor,
655
- whose child it is, and the coordinate this packet's response becomes, so `reasoning://<worker>/L/T`
656
- and `ops://<worker>/L/T` are the model's own and `log:///L/T/*` its rows; a model never infers the
657
- present from the last row's coordinate, which may or may not be its own turn. The block
658
- changes every turn, so nothing of it precedes the log, and the packet carries no date, time
659
- or zone anywhere. The coordinate only; the source addresses stay
660
- documented, not taught.
718
+ `{"path": "worker://<name>", "parent": <address or null>, "loop": L, "turn": T, "owner":
719
+ {"address": "<owner>", "interactive": <boolean>}}`: the actor, whose child it is, the coordinate
720
+ this packet's response becomes, so `reasoning://<worker>/L/T` and `ops://<worker>/L/T` are the
721
+ model's own and `log:///L/T/*` its rows, and its approval owner ({§worker-ownership}) with whether
722
+ a person attends that owner; a model never infers the present from the last row's coordinate,
723
+ which may or may not be its own turn, and never infers from a missing tool alone that nobody can
724
+ answer. The block changes every turn, so nothing of it precedes the log, and the packet carries no
725
+ date, time or zone anywhere. The coordinate only; the source addresses stay documented, not
726
+ taught.
661
727
 
662
728
  Worker control rides the daemon's inject seam (active→fold, idle→enqueue+drain), so the handler creates/branches the worker and hands off; the daemon owns provider + system prompt. FORK/WORK carry the seed task in the body and are their own ops, dispatched to worker control — never the entry-copy path.
663
729
 
@@ -864,7 +930,7 @@ pass.
864
930
 
865
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`.
866
932
 
867
- **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.
868
934
 
869
935
  **Schema.** The version-1 baseline stores the normalized {§inference-ledger},
870
936
  its model-response evidence, emission admission, and cardinal
@@ -1141,7 +1207,7 @@ boundary.
1141
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.
1142
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.
1143
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.
1144
- - §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.
1145
1211
 
1146
1212
  ---
1147
1213
 
@@ -1238,7 +1304,9 @@ alone are not a cycle: changing inputs or observations distinguish activity.
1238
1304
  A turn that executed nothing has no activity to identify, so an empty turn's
1239
1305
  identity is its **text** ({§empty-turn}) — the same principle, applied to the only
1240
1306
  output it produced. Identifying it by its absent program instead makes every empty
1241
- 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}).
1242
1310
  This is an exact-repetition backstop, not a semantic judgment of task progress;
1243
1311
  new asynchronous invocation identities do not prove repetition of their eventual
1244
1312
  effects. Ordinary contract strikes and operator budgets remain independent.
@@ -1309,12 +1377,13 @@ independent turn ceiling terminates at **429** ({§loop-terminals}). The streak
1309
1377
  and cycle verdict are absent from model packets; only the concrete occurrences
1310
1378
  in the table are shown. The streak never leaves the daemon.
1311
1379
 
1312
- A crossing terminal names the source that struck the crossing turn — `repetition`,
1313
- `no_operation`, then `operation` — in its detail, in that order when a turn matches more
1314
- 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*
1315
1383
  one, and reporting it as a failed turn misreads a model answering without the fence as a model
1316
1384
  whose operations broke. A cycle of empty turns names repeated responses without operations,
1317
- 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
1318
1387
  rail rules on the crossing and does not retain the kinds behind it. What the crossing turn
1319
1388
  actually said is cited, not discarded ({§terminal-evidence}). Naming the source is not the
1320
1389
  private accounting {§rail-accounting-private} withholds: the streak, the cycle verdict and
@@ -1330,6 +1399,7 @@ restart; curation of log evidence cannot alter them.
1330
1399
  | Assessed turn with a violation | Increment once. | Include its exact activity. |
1331
1400
  | Clean assessed turn | Reset to zero. | Include its exact activity. |
1332
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. |
1333
1403
  | Recoverable provider outage | No assessment; preserve the streak. | Preserve until an actual park. |
1334
1404
  | New loop | Start at zero. | Start empty. |
1335
1405
 
@@ -1411,7 +1481,9 @@ completion before the model sees them in the next packet.
1411
1481
  WAIT without a live obligation continues to those results.
1412
1482
  This is operation recovery, not provider
1413
1483
  resampling. A malformed statement's Problem records the factual
1414
- `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.
1415
1487
 
1416
1488
  §invalid-emission-attempts Each exhausted emission-attempt budget contributes
1417
1489
  one frame-contract strike to the same streak as other contract violations
@@ -1655,7 +1727,7 @@ Every fact names the canonical key, never the host root or an echo of the
1655
1727
  model's spelling. These classes let a caller distinguish a wrong address, an
1656
1728
  invalid range, read-only authority, and occupied hidden state without guessing.
1657
1729
 
1658
- §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 `{"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}).
1659
1731
 
1660
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.
1661
1733
 
@@ -1680,6 +1752,9 @@ cross-scheme SQL transaction. Core's create-only publication claims the same
1680
1752
  identity atomically: an existing identity returns 409 without changing its
1681
1753
  metadata or channels.
1682
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
+
1683
1758
  ### Op methods
1684
1759
 
1685
1760
  §op-methods-op-dispatch Engine operation ownership follows the public scheme contract:
@@ -1697,7 +1772,7 @@ Registration precedes loop affinity:
1697
1772
  | Registered but inactive under flag | The flag gate returns `403 scheme-unavailable`. |
1698
1773
  | Registered and active | Dispatch continues to the operation owner. |
1699
1774
 
1700
- - §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.
1701
1776
 
1702
1777
  §bare-inference **BARE is isolated, synchronous retrieval over the durable child-provider policy.**
1703
1778
 
@@ -1711,7 +1786,7 @@ Registration precedes loop affinity:
1711
1786
  | Failure | A source/admission/provider failure affects its own operation, not successful siblings. Accounting or persistence failure is internal and fails hard. |
1712
1787
  | Observation | Responses are unseen retrieval work; completion follows {§send-premature-terminate}. |
1713
1788
 
1714
- - §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.
1715
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.
1716
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.
1717
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.
@@ -2111,7 +2186,7 @@ source line therefore retains the same prefix across projections of one revision
2111
2186
  An explicit default-channel fragment and its fragmentless spelling share that
2112
2187
  identity; a selected non-default channel retains its canonical `#channel`.
2113
2188
 
2114
- 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
2115
2190
  anchor against the addressed current complete content before applying the
2116
2191
  ordinary numeric text-coordinate contract. Exactly one current match lowers to
2117
2192
  its numeric line; zero or multiple matches return 409 `line-anchor-collision`,
@@ -2388,6 +2463,32 @@ The `## Log` section is a sequence of ordinary Markdown records separated by one
2388
2463
 
2389
2464
  An emission row's body is not in its record ({§emission-row}); its eligible program follows it as a separate assistant message ({§emission-history}). Patterns retain their literal spelling; a pattern containing a line break is JSON-quoted to keep the H3 on one physical line. Receipts are descriptive records, not reconstructed operation headings. Absent fields are not invented. Every physical body line retains its canonical numeric `N:` or anchored `@hash N:` coordinate, so source text cannot create a record boundary. The section contains records only, with no leading prose or enclosing fence.
2390
2465
 
2466
+ §log-mutation-patch **A settled file mutation's row keeps what it did.** When an accepted EDIT,
2467
+ COPY, MOVE or KILL writes or removes a text member, the file scheme appends the unified diff of
2468
+ that write, `current` to `proposed`, to the operation row it is settling (`log_entries.patch`); a
2469
+ row that settles several writes keeps them in application order. The row serves it as its `patch`
2470
+ channel, `log:///<loop>/<turn>/<item>/<OP>#patch`, `text/x-diff`, for the life of the row: a fork
2471
+ copies it with the row, and it never enters a receipt or a packet unasked. Nothing is kept for a
2472
+ byte write, a member past the materialization bound, or a row settled before this version, and
2473
+ nothing is reconstructed for them: such a row has no `patch` channel, and whatever reads one, a
2474
+ rollback included, fails by name rather than inventing one.
2475
+
2476
+ §rollback **Rollback reverses a worker's settled file mutations, latest first, through ordinary
2477
+ operations.** `rollback({ workspaceId, workerId, actor, to })` (`workspace.rollback { to,
2478
+ workerId? }` for a client) names a log coordinate of `workerId`, `<loop>/<turn>/<item>`; the state
2479
+ after that item is what remains. Every settled EDIT, COPY, MOVE or KILL row after it, latest first,
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
2485
+ kept no patch or were reversed already (the file is in the state before that mutation, as an earlier
2486
+ rollback left it), and where it stopped: `drift`, the file no longer holds what that mutation wrote,
2487
+ leaves that file and everything earlier untouched; `refused` carries the reversing operation's
2488
+ Problem. A coordinate that is not one of the worker's rows is `404 coordinate-not-found`, a
2489
+ malformed one `400 coordinate-invalid`, and a workspace without a project root `400
2490
+ project-root-required`. Nothing is reconstructed for a patchless row, and the log is never rewritten.
2491
+
2391
2492
  §log-address-metadata **Addresses name their relationship, not the row's producer.**
2392
2493
 
2393
2494
  | Spelling | Meaning | Where |
@@ -2438,9 +2539,9 @@ complete non-retrieval body retains `lines` where no other field supplies its
2438
2539
  navigable extent. None of these spellings changes acquisition, delivery,
2439
2540
  curation, admission, or immutable evidence.
2440
2541
 
2441
- 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}.
2442
2543
 
2443
- 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.
2444
2545
 
2445
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.
2446
2547
  - §packet-attachment-parts A successful READ of an attachable resource carries projection facts with its
@@ -2979,10 +3080,27 @@ When all messages are resolved but new results require another packet, the engin
2979
3080
  reports only: `All Open Messages resolved. Review new results; emit no OPs if finished.`
2980
3081
  The notice creates no state and does not demand another final reply.
2981
3082
 
2982
- §worker-obligations A worker holds its unresolved children and open non-detached
2983
- streams (`worker_obligations`); `loop_obligations` names them per loop. The
2984
- packet's Delegation list, WAIT, completion and drain wake settlement use that
2985
- same durable liveness.
3083
+ §worker-obligations A worker holds its unresolved children, its open non-detached
3084
+ streams, and the messages it sent to other workers that await completion while the
3085
+ recipient's loop is live (`worker_obligations`); `loop_obligations` names them per
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
3092
+ that same durable liveness: a worker that messaged another worker parks on an
3093
+ authored WAIT until the completion arrives ({§message-completion}), and with no WAIT
3094
+ joins that work with the bounded park rather than concluding idle.
3095
+
3096
+ §message-abandonment **An ended loop answers what it left open.** When a loop ends,
3097
+ every message another worker sent it that has no completion is completed `499` by the
3098
+ daemon: one `_plurnk` SEND to the message's address in a maintenance turn of the
3099
+ recipient, "Loop *N* ended with status *S*; this message was not answered.",
3100
+ delivered to the sender like any reply ({§message-reply-delivery}). A message the
3101
+ ending loop had not yet published is re-homed first ({§message-loop-containment}) and
3102
+ is not abandoned. The sender's hold ends with the recipient's loop either way; the
3103
+ completion is what wakes it and tells it.
2986
3104
 
2987
3105
  §wait-obligation-matrix **End-of-program resolution.** Execute all admitted operations and settle optimistic work before applying this table. WAIT is an optional yield, not an end-of-program delimiter.
2988
3106
 
@@ -3078,7 +3196,7 @@ accounting and model-visible failure evidence remain separately owned by
3078
3196
  The strike sends no notice of its own: the turn records one `_plurnk`
3079
3197
  error row, `422` `The turn performed no operation.`, which rides the next packet's errors like
3080
3198
  any failure ({§operation-result-uniform-error-channel}); the threshold terminal still says why ({§engine-rails}).
3081
- 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});
3082
3200
  different empty programs are not a repeated cycle merely because neither contained
3083
3201
  operations. Lost-boundary handling remains {§unparsed-tail-boundary}; no confirmation
3084
3202
  token or private retry is invented here.
@@ -3144,7 +3262,9 @@ of which Worker launched it ({§execution-output-identity}).
3144
3262
  | --- | --- |
3145
3263
  | Admission | Require SEND control and the original invocation's runtime/tool capabilities. Preserve its classified effect: host input proposes; pure/read input uses the same effect policy as launch. |
3146
3264
  | Acceptance | Recheck both capabilities, workspace, and the exact live subscription. Stale approval cannot reach a replacement invocation. |
3147
- | Queued or starting without a receiver | `409 input-unavailable`, immediately; never wait for an execution slot while blocking another input-dependent execution. |
3265
+ | Launched without input | The invocation's preparation declared no receiver ({§executor-live-input}): `409 input-unavailable`, immediately, naming the launch form that opens input. |
3266
+ | Queued for a slot | `409 input-unavailable`, immediately and `retryable`; never wait for an execution slot while blocking another input-dependent execution. |
3267
+ | Admitted, receiver declared, not yet open | Delivery waits for the receiver to register, within the backpressure bound; the execution settling first returns `410 input-closed`. A receiver registered without the declaration is the executor's defect and fails its run. |
3148
3268
  | Unknown address | `404 stream-not-found`. A terminal execution or retired input returns `410 input-closed`. |
3149
3269
  | Delivery | Serialize accepted inputs per invocation. Preserve authored body and receiver-owned metadata. Receiver success means delivery only, not completion. |
3150
3270
  | Backpressure | `PLURNK_SERVICE_EXEC_INPUT_TIMEOUT_MS` bounds each accepted delivery, including its queue residence. Expiry returns `504 input-timeout`; cancellation returns `499 input-cancelled`. Once delivery has begun, retire and abort input on either; delivery may be partial and is never replayed. The execution itself is not implicitly killed. |
@@ -3282,8 +3402,12 @@ unchanged for proposal policy, application, stream registration, and
3282
3402
  effect-qualified hold policy. The post-acceptance materialization path never
3283
3403
  triggers reclassification.
3284
3404
 
3285
- §exec-registry-resolves The runtime slot (`signal`) selects an executor from
3286
- 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:
3287
3411
  they are discovered and probed at startup, and availability is cached.
3288
3412
  Workspace Functionality providers may atomically overlay additional names under
3289
3413
  {§module-workspace-capabilities}; a name has one owner within a workspace, while
@@ -3319,7 +3443,7 @@ execution is refused `scope-unsupported` (400).
3319
3443
  | `lifetime` | The spawn |
3320
3444
  | ------------ | --------- |
3321
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. `` |
3322
- | `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}). |
3323
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}). |
3324
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. |
3325
3449
 
@@ -3365,18 +3489,53 @@ lifecycle teardown still reaps its work. No new delegation semantics follow.
3365
3489
 
3366
3490
  §worker-auto-name **Automatic names are eight random lowercase hexadecimal
3367
3491
  characters**, e.g. `worker://ab3d5678`; a colliding draw is retried. All unnamed
3368
- workers use this allocator, including WORK, FORK, clients, and conversations.
3492
+ workers use this allocator, including WORK, FORK, clients, and model workers.
3369
3493
  Names encode neither machine identity nor topology or role; those remain
3370
3494
  separate durable fields.
3371
3495
  Automatic names never reuse an existing literal. Name selection and worker
3372
3496
  creation share one atomic claim: concurrent allocations receive distinct
3373
- addresses, while concurrent default-conversation ensures converge on one root
3374
- worker. Generated addresses appear in ordinary operation receipts and child
3497
+ addresses. Generated addresses appear in ordinary operation receipts and child
3375
3498
  inventory; allocation never rewrites the submitted operation's target.
3376
3499
 
3377
- §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.
3378
-
3379
- §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.
3380
3539
 
3381
3540
  §effect-policy-tunable **Effect admission is one knob per effect.**
3382
3541
  `PLURNK_SERVICE_EFFECT_HOST`, `PLURNK_SERVICE_EFFECT_READ` and
@@ -3392,7 +3551,7 @@ two states and no others:
3392
3551
 
3393
3552
  | state | what the model receives |
3394
3553
  |---|---|
3395
- | 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. |
3396
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}). |
3397
3556
 
3398
3557
  §stream-observation-result **One liveness fact.** The durable READ result owns
@@ -3407,7 +3566,7 @@ publishes only once subscription settlement has installed its terminal result;
3407
3566
  until then the stream is still in flight and publishes on the turn after it settles.
3408
3567
 
3409
3568
  §exec-concurrency **Bounded admission per workspace (#389).** At most
3410
- `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`
3411
3570
  unbounded); the scope is the workspace, so neither delegation nor later turns
3412
3571
  bypass it and no other workspace can starve it. Every admitted execution still creates its
3413
3572
  entry, channels, and open subscription before its receipt returns, so queued work is
@@ -3419,7 +3578,8 @@ it: an immediate slot is `200 { outcome: "started" }`; delayed work is
3419
3578
  current truth. Admission is FIFO within the workspace; queue residence does not consume
3420
3579
  the execution timeout; a KILL while queued never invokes the executor and concludes the
3421
3580
  stream as any KILL does ({§stream-control}). The scheduler is the exec scheme's; the knob is the
3422
- 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.
3423
3583
 
3424
3584
  §exec-stream-page **An unrequested delivery never exceeds the retrieval page.** The
3425
3585
  terminal observation is the same page a markerless READ returns, whatever the
@@ -3434,7 +3594,7 @@ records stop at the last complete line that fits, or at an exact Unicode region
3434
3594
  when even the first line exceeds the character bound. Explicit READ scopes stay exact.
3435
3595
 
3436
3596
  The durable per-subscription, per-channel cursor records the size last reported
3437
- 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
3438
3598
  observation at close — so the pointer can state growth and no partial document
3439
3599
  or record ever reaches the model. The terminal observation and its cursor
3440
3600
  transition commit atomically. A concluded stream lands one conclusion row per
@@ -3578,6 +3738,9 @@ Core derives the contracts-owned `ProposalProjection` from the durable proposed
3578
3738
  | Projection field | Durable authority |
3579
3739
  | --------------------- | ------------------------------------------------------------------------------------------------------------------ |
3580
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 |
3581
3744
  | `target` | canonical `{ scheme, authority, pathname }` from `attrs.proposalTarget` for staged COPY/MOVE, otherwise the log row target |
3582
3745
  | `body` | proposed operation result `rx.body`; absent means the empty review body |
3583
3746
  | `attrs` | proposed log row `attrs` object |
@@ -3631,6 +3794,7 @@ operation, message sender, or attrs.
3631
3794
  | Runtime bookkeeping | reject, `runtime_bookkeeping` |
3632
3795
  | Server `accept` | accept |
3633
3796
  | Server `reject` | reject, `policy_veto` |
3797
+ | Server `review`, a person's client operation ({§connection-lifecycle}) | accept, `client_operation` |
3634
3798
  | Server `review`, owner implements `request_approval` | review |
3635
3799
  | Server `review`, owner has no reviewer | reject, `no_review_channel` |
3636
3800
 
@@ -3638,8 +3802,9 @@ Core selects the disposition once for the pending gate. Its live event and
3638
3802
  reconnect projection use that same decision. Automatic settlement precedes
3639
3803
  observational notification; an observer failure cannot change authority or orphan
3640
3804
  a waiter. Invalid configuration is diagnosed at the proposal boundary without
3641
- substituting authority ({§configuration-repair-path}). Client-authored
3642
- 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.
3643
3808
 
3644
3809
  §runtime-bookkeeping-policy Runtime bookkeeping cannot acquire new authority.
3645
3810
  Generated reference publication and audit narration have no reviewer and do not
@@ -4083,6 +4248,13 @@ systemd user unit. Installation and enablement are explicit operator actions;
4083
4248
  package installation performs neither. The template documents executable-path
4084
4249
  and environment adjustments instead of introducing a service-management command.
4085
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
+
4086
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`.
4087
4259
 
4088
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.
@@ -4092,7 +4264,7 @@ Each knob's value lives on its panel and nowhere else (`plurnk-service config de
4092
4264
  | `PLURNK_SERVICE_DB_PATH` | SQLite file path; an explicit non-empty value overrides the derived default. |
4093
4265
  | `PLURNK_SERVICE_SQLITE_SYNCHRONOUS` | Connection-time WAL durability: `OFF`, `NORMAL`, `FULL` or `EXTRA` ({§db-durability}). |
4094
4266
  | `PLURNK_SERVICE_SHARE_FOLDER` | Parent of the shares written when no folder is named ({§share-folder}); empty is `$XDG_STATE_HOME/plurnk/shares`. |
4095
- | §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. |
4096
4268
  | §operator-config-git-ceiling `PLURNK_SERVICE_GIT_ALLOWED` | Hard service ceiling: only `1` admits Git membership and status; every other value denies them. |
4097
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. |
4098
4270
  | `PLURNK_SERVICE_FILE_MATERIALIZE_MAX_BYTES` | Byte ceiling in `1..104857600` for one workspace-file snapshot ({§membership-materialization-limit}). |
@@ -4103,7 +4275,7 @@ Each knob's value lives on its panel and nowhere else (`plurnk-service config de
4103
4275
  | `PLURNK_SERVICE_PROVIDER_RECOVERY_BACKOFF` | First recovery delay (ms); doubles per failure up to `PLURNK_SERVICE_PROVIDER_RECOVERY_BACKOFF_MAX` ({§provider-recovery}). |
4104
4276
  | `PLURNK_SERVICE_MAX_STRIKES` | Consecutive turn-contract strike threshold ({§engine-rails}). |
4105
4277
  | `PLURNK_SERVICE_EMISSION_ATTEMPTS` | Completed provider responses allowed beneath one engine turn before frame admission is exhausted. Bounded interior operation errors are admitted without spending this budget. Exhaustion contributes one frame-contract strike under {§invalid-emission-attempts}. |
4106
- | `PLURNK_SERVICE_BUDGET_PRESSURE` | Budget percentage above which the gauge names its largest rows ({§context-gauge}) and a packet within its budget carries the `budget_pressure` notice ({§context-pressure-notice}); measured before adding either. |
4278
+ | `PLURNK_SERVICE_BUDGET_PRESSURE` | Budget percentage above which the gauge names its largest rows ({§context-gauge}); measured before adding the inventory. |
4107
4279
  | `PLURNK_SERVICE_BUDGET_LARGEST_ITEMS` | How many of the heaviest retained log rows the pressured gauge names ({§context-gauge}). |
4108
4280
  | `PLURNK_SERVICE_LINE_ANCHOR_CONTEXT_LINES` | Minimum neighboring lines hashed on each side; repeated handles expand their context ({§line-anchor-disambiguation}). |
4109
4281
  | `PLURNK_SERVICE_EDIT_RECEIPT_CONTEXT_LINES` | Surrounding and landed lines shown at each EDIT result boundary ({§edit-result-receipt-projection}). |
@@ -4279,7 +4451,7 @@ flowchart LR
4279
4451
  release --> warm["Bounded warm grace / idle LRU"]
4280
4452
  warm -->|new demand| lease
4281
4453
  warm -->|idle bound| cool["Deactivate providers;<br/>withdraw runtime projections"]
4282
- interface -->|worldless call| calls
4454
+ interface -->|daemon call| calls
4283
4455
  calls --> core["Core state and orchestration"]
4284
4456
  core --> events["subscribeToEvents<br/>(workspaceId, event, payload)"]
4285
4457
  events --> interface
@@ -4293,7 +4465,8 @@ modules in registration order. The daemon's listener is bound before durable adm
4293
4465
  ({§module-http-mounts}). Persisted workspaces with
4294
4466
  no durable work stay passive until first demand; activation publishes their
4295
4467
  complete capabilities and documentation before the demanding operation
4296
- 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
4297
4470
  with Core: recovery may demand a workspace provider before `start`. Requests on
4298
4471
  the listener remain unavailable until it is admitted; a module opens any other exterior
4299
4472
  ingress only after recovery. No
@@ -4334,11 +4507,12 @@ An invalid module object, factory result or lifecycle member is an implementatio
4334
4507
  not configuration, and fails loudly. What a module contributes registers through its owning public
4335
4508
  interfaces and is released during {§module-lifecycle} resource teardown.
4336
4509
 
4337
- §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
4338
4512
  aborts proposals, branches, derivations, and worker scopes. It begins module
4339
4513
  `stop()` calls in reverse registration order without serially awaiting them,
4340
4514
  so every producer is asked to stop even if another stalls. Core settles drains,
4341
- capability publications, module producers, streaming producers, derivations,
4515
+ whole workspace activation and cooling transitions, capability publications, module producers, streaming producers, derivations,
4342
4516
  mimetypes, schemes, and the final worker-settlement barrier while observers
4343
4517
  remain subscribed. Only then does it begin and join module `close()` calls in
4344
4518
  reverse order. Failures do not skip later phases and join one shutdown aggregate.
@@ -4349,6 +4523,9 @@ After asynchronous selection, the supervisor rechecks shutdown before creating
4349
4523
  a drain or installing a timer; parked-loop wake mutations also recheck worker
4350
4524
  cancellation under {§worker-lifecycle-durable-disposition}.
4351
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.
4352
4529
  The shared deadline bounds every phase, including observer delivery; forced
4353
4530
  shutdown may therefore lose notifications and reports the unfinished phase.
4354
4531
 
@@ -4369,7 +4546,8 @@ flowchart LR
4369
4546
  stop[Begin stop] --> abort[Abort core producers]
4370
4547
  stop --> moduleStop[Begin reverse module stop]
4371
4548
  abort --> drains[Settle worker drains]
4372
- drains --> joined[Settle module producers]
4549
+ drains --> residency[Join workspace transitions<br/>and capability publications]
4550
+ residency --> joined[Settle module producers]
4373
4551
  moduleStop --> joined
4374
4552
  joined --> producers[Settle streaming producers]
4375
4553
  producers --> resources[Dispose derivations,<br/>mimetypes, and schemes]
@@ -4386,6 +4564,8 @@ published module contract.
4386
4564
 
4387
4565
  | Setup function | Contract |
4388
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. |
4389
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. |
4390
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. |
4391
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}). |
@@ -4428,7 +4608,10 @@ Settling the coordinator includes accepted refreshes waiting for that gate,
4428
4608
  not only work already inside a family lane.
4429
4609
  Explicit client mutations use `try` and fail 409 before effects while the workspace is held.
4430
4610
  Refused mutations reserve no queue position; a client retry is a new admission.
4431
- 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
4432
4615
  their already-held demand boundary. Providers reject replacement while active
4433
4616
  requests, input exchanges, or Tasks depend on the old snapshot; infrastructure
4434
4617
  watches may be replaced. These are concurrency constraints, not creator privileges.
@@ -4450,8 +4633,11 @@ bound. Concurrent demand coalesces; cooling never closes a leased connection.
4450
4633
 
4451
4634
  Cooling withdraws live capability and releases process resources at the
4452
4635
  workspace boundary. Durable configuration, history, and saved entries remain.
4453
- New demand reconstructs the one shared snapshot. Shutdown cancels warm timers
4454
- 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.
4455
4641
 
4456
4642
  §module-workspace-residency-facet **Residency is the workspace's; processes are
4457
4643
  one family's facet.** The workspace is the lease unit, and every family's
@@ -4503,6 +4689,20 @@ It neither removes the old definition first nor merges omitted fields.
4503
4689
  | Authorization required | Publish the existing authorization-required handoff; this is not a failed preparation. |
4504
4690
  | Identical definition | Retain enabledness and normal attachment reuse; this is not an explicit enable/restart. |
4505
4691
 
4692
+ §functionality-addressing **A family may name definitions by their content.** An adapter that
4693
+ declares `addressing` supplies the input of `add` and the input of `enable`, `disable` and `remove`,
4694
+ and reads from a select input the alias it names; its alias grammar ({§functionality-adapter}) is
4695
+ the identity's own, and the generated teaching's `add` example is that input. Every other rule of
4696
+ the coordinator — ownership, enabledness, `alias-exists`, `alias-unknown`, publication, documents —
4697
+ reads the alias as that identity. The members family names its definitions by their glob
4698
+ ({§members-functionality}).
4699
+
4700
+ | Verb | Addressing family |
4701
+ |---|---|
4702
+ | `add` | Admits every definition the body names, or none: one refusal refuses the whole body. Reapplying a definition the scope holds is idempotent; a different definition under the same name is `alias-exists` (409), whose recovery is remove and add. The receipt lists the admitted definitions in body order (`definitions`) and names no one `alias`. Preparation receives them all as its admissions. |
4703
+ | `replace` | Not served: when the name is the definition there is nothing to reassign, so neither projection offers it ({§functionality-discover-advertisement}). |
4704
+ | `enable`, `disable`, `remove` | Take the family's select input; the receipt names the one alias it concerned. |
4705
+
4506
4706
  §functionality-mutation-status **Mutation status describes the committed resource
4507
4707
  outcome, not the management invocation's stream lifetime.** Apply these rows in
4508
4708
  order; refusals retain their own Problems and do not commit a replacement.
@@ -4510,10 +4710,10 @@ order; refusals retain their own Problems and do not commit a replacement.
4510
4710
  | Outcome | Status |
4511
4711
  |---|---:|
4512
4712
  | The resulting definition requires authorization | 202 |
4513
- | `add` or `replace` creates a local definition, including an override of an inherited definition | 201 |
4514
- | `add` reapplies the identical local definition; `replace` assigns an existing local definition; `enable`, `disable`, or `remove` settles | 200 |
4713
+ | `add` or `replace` creates a local definition, including an override of an inherited definition; an `add` naming several ({§functionality-addressing}) creates any of them | 201 |
4714
+ | `add` reapplies the identical local definition, every one it names; `replace` assigns an existing local definition; `enable`, `disable`, or `remove` settles | 200 |
4515
4715
 
4516
- `add` of a different local definition remains `alias-exists` (409); `replace` is explicit.
4716
+ `add` of a different local definition remains `alias-exists` (409); `replace` is explicit where a family serves it.
4517
4717
  Model invocation settlement follows {§functionality-model-mutation} independently.
4518
4718
 
4519
4719
  §functionality-discover-advertisement **A family's `discover` advertises exactly the inputs it
@@ -4616,7 +4816,7 @@ state without demand; `workspace/preparation` broadcasts
4616
4816
  | Boundary | Visible state |
4617
4817
  |---|---|
4618
4818
  | Preparation begins | Family, `phase: preparing`, `alias: null`, UTC `since` |
4619
- | 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 |
4620
4820
  | Prepared candidate enters publication | `phase: publishing`, `alias: null` |
4621
4821
  | Commit, rejection, or rollback settles | Family removed; empty array means no preparation |
4622
4822
 
@@ -4727,7 +4927,8 @@ reserved or masked name is no resource. The family's manager exposes it as the f
4727
4927
  the value its commands receive now, FIND lists the names in effect and a pattern on it matches
4728
4928
  values, COPY carries a value out. The manager's own output streams, eight hexadecimal characters
4729
4929
  ({§execution-output-identity}), stay the executor's. A write is the Worker's own layer, never the
4730
- 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
4731
4932
  Worker, and KILL retires the name from the Worker's commands — `remove` for an entry the Worker
4732
4933
  owns, which restores an inherited value, `disable` for an inherited one, which keeps its
4733
4934
  definition. Admission is the family's ({§env-functionality}) and its refusal is the operation's
@@ -4739,7 +4940,9 @@ effect is `entry-not-found` (404) on READ and KILL alike.
4739
4940
  §functionality-scope **A family declares its supported scopes.** Skills, MCP, members
4740
4941
  and outbound A2A describe what exists in a **workspace**: a capability, resident or installable,
4741
4942
  that every Worker there shares. Environment describes how one **Worker** works — context rather
4742
- 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
4743
4946
  `scopes` (absent means `["workspace"]`; the first is the model default), and the
4744
4947
  coordinator keys durable state and the locally-owned `origin`
4745
4948
  by it: a workspace-scoped family's own entries carry origin `workspace`, a worker-scoped
@@ -4826,20 +5029,27 @@ A family that ships no file has a header-only document. Registration also
4826
5029
  validates the adapter's taught `add` example against the `add` input schema it
4827
5030
  teaches, so a wrong example fails boot rather than the model.
4828
5031
 
4829
- §functionality-model-mutation **An accepted mutation completes through its ordinary
4830
- execution stream.** Preparation and publication share the family's serialized
4831
- lane. Publication acquires workspace exclusivity after current turns release
4832
- 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.
4833
5041
  It then reports `active`, `unavailable`, or `authorization-required`, or the exact
4834
5042
  publication failure. Resource readiness is not execution liveness: a mutation's
4835
5043
  `202 authorization-required` body remains exact, while the finished manager
4836
5044
  execution closes with `200`. It does not wait for sign-in. A preparation failure may publish enabled-but-unavailable
4837
- state for other mutations, but `replace` rejects it ({§functionality-replacement}); a publication failure never reports a successful mutation. Stream polling,
4838
- 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
4839
5046
  separate deferred-commit queue. An explicit client action publishes now, rejects
4840
5047
  a failed preparation, and fails 409 while the workspace is held
4841
5048
  ({§module-workspace-quiescence}). Rejecting a proposal prepares, persists, and
4842
- 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}).
4843
5053
 
4844
5054
  ## §methods Application interface
4845
5055
 
@@ -4851,34 +5061,36 @@ Core's behavior behind them.
4851
5061
  | Area | Function | Core contract |
4852
5062
  |---------------------------------------------------|----------|---------------|
4853
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. |
4854
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. |
4855
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. |
4856
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. |
4857
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. |
4858
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. |
4859
- | §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. |
4860
- | §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. |
4861
- | 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}). |
4862
- | §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`. |
4863
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. |
4864
5075
  | Providers | `listProviders()` | Lists configured aliases with provider/model identity, active state, and the effective provider-derived `inputCapacity` when known. |
4865
5076
  | Model catalog | `listModels(query)` | Returns one validated bounded {§model-catalog-wire} page under {§model-catalog}; performs no provider request or selection. |
4866
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. |
4867
- | §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. |
4868
- | §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. |
4869
- | §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. |
4870
- | §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}). |
4871
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. |
4872
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. |
4873
5084
  | Runtime actor | `ensureRuntimeWorker(workspaceId)` | Returns the workspace's `_plurnk` actor, creating it if absent; it always retains runtime ownership. |
4874
- | Workspace lifecycle | `forkWorker({ workspaceId, workerId, name? })` | Creates a child worker that branches the source worker's history while sharing workspace state. |
4875
- | §methods-workspace-rename Workspace metadata | `renameWorkspace(workspaceId, name)` | Changes only the world's unique mutable name; workers, log, and membership remain intact. |
4876
- | §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`. |
4877
- | Workspace metadata | `listWorkspaces()`, `workspaceDerivationStatus(...)` | Reads current workspace identity and derivation progress. |
4878
- | §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. |
4879
- | §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). |
4880
- | §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. |
4881
- | §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. |
4882
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. |
4883
5095
 
4884
5096
  §methods-loop-run-fold-consistency **A folded prompt cannot silently reconfigure
@@ -4914,19 +5126,17 @@ If an unpublished message is promoted into subsequent work under
4914
5126
  {§message-loop-containment}, its selected paths travel with it.
4915
5127
 
4916
5128
  §methods-rebind **Binding belongs to the client-interface module.** Core's
4917
- workspace lifecycle calls return exactly the workspace and selected client actor
4918
- (`workspaceId`, `workspaceName`, `projectRoot`, `workerId`, `workerName`); they
4919
- carry no conversation-worker or action-loop binding. Core retains no connection,
4920
- thread, or current-workspace mapping. A module may replace its own binding with a
4921
- later create or attach result without requiring a new transport; it resolves the
4922
- conversation worker separately, while each client action allocates its own
4923
- administrative loop under {§connection-lifecycle}.
4924
-
4925
- §methods-worker-name-admission **Client worker-name admission.** Attach,
4926
- fresh-conversation, and fork apply {§worker-name-minting} before lookup or
4927
- creation. A client therefore cannot forge or resume the runtime actor (its
4928
- name lies outside `WORKER_NAME`), insert a non-mintable spelling, or make the
4929
- 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.
4930
5140
 
4931
5141
  §capability-admission **One admission path owns external authority.** Core
4932
5142
  derives one or more `CapabilityDescriptor` demands from each routed statement,
@@ -4963,7 +5173,7 @@ resolver.** Workspace capability actions return the contracts-owned
4963
5173
  their normalized effective intersection. The same resolver governs dispatch
4964
5174
  and packet shaping. Replacement preserves unrelated workspace settings. Before their next operation
4965
5175
  or observation, existing workers reconcile generated references against it. It requires no selected
4966
- conversation worker and returns the complete fresh projection.
5176
+ bound worker and returns the complete fresh projection.
4967
5177
 
4968
5178
  §question-tool **The native request-user-input tool.** Core registers one
4969
5179
  in-process `question` runtime at boot and offers it only to a worker whose owner
@@ -5026,7 +5236,7 @@ packets or state snapshots.
5026
5236
  owns one durable model, persisted as a nullable `model_routes` foreign key.
5027
5237
  Concurrent first selections of the same resolved tuple share one identity;
5028
5238
  an insertion conflict returns the existing route without changing its fields.
5029
- 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
5030
5240
  the daemon default — and never re-seeded from a later default change. A
5031
5241
  deliberately modelless daemon leaves the worker unset and rejects model work
5032
5242
  until an explicit selection. Starting a loop snapshots the worker's resolved
@@ -5035,7 +5245,7 @@ continue from the loop snapshot and never re-resolve through the alias
5035
5245
  cascade. A WORK/FORK child copies the spawning loop's effective spawn model
5036
5246
  (spawn override ?? model) onto the new worker by value at creation; it retains
5037
5247
  no live link and begins with no override, so a later parent change affects
5038
- 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
5039
5249
  Plurnk-owned bookkeeping workers run no model loops and own no model
5040
5250
  selection; the model, spawn-override, and effort controls refuse them with
5041
5251
  `409 model-worker-required` before any policy row is initialized or written. An explicit model, spawn-override, or effort change while
@@ -5117,26 +5327,40 @@ beside database ids, so a client can render and resolve the logical `L/T/S`
5117
5327
  coordinate without fetching all rows and matching locally.
5118
5328
 
5119
5329
  §methods-log-entry-wire **Log entry wire fidelity.** `readLog` and `log/entry`
5120
- 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.
5121
5332
  Client interfaces do not reconstruct these fields from operation or origin.
5122
5333
 
5123
5334
  §methods-readable-reasoning **Readable provider reasoning remains derived
5124
- provider evidence.** On model SEND and disposition rows, `readLog` and `log/entry`
5125
- project a nonempty admitted `packet.assistant.reasoning` as the optional
5126
- `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;
5127
5341
  core does not copy readable reasoning into log attributes or bodies. A turn
5128
5342
  without readable reasoning omits the field.
5129
5343
 
5130
- §op-look **LOOK ownership.** A client-interface module owns the public LOOK
5131
- spelling and grammar parsing. It rewrites a valid LOOK statement to READ and
5132
- hands the AST to core's `look`; core owns the full resolver and the no-log
5133
- invariant. The internal closed, rowless observation segment supplies an honest
5134
- numeric loop coordinate for relative `log:///` addressing without leaving
5135
- active lifecycle behind. The segment belongs to the acting worker (`workerId`);
5136
- the READ resolves `log:///` as `perspectiveWorkerId` when one is given; explicit source
5137
- authorities retain their identity under {§turn-source-resources}. A client can look at a conversation without
5138
- adding a loop to it. LOOK text anchors resolve through the same
5139
- {§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.
5140
5364
 
5141
5365
  ### §notifications Core events
5142
5366
 
@@ -5144,13 +5368,13 @@ adding a loop to it. LOOK text anchors resolve through the same
5144
5368
  |--------------------------------------------------------------|---------|------------|
5145
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. |
5146
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. |
5147
- | §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. |
5148
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. |
5149
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}. |
5150
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. |
5151
5375
  | §notifications-workspace-created `workspace/created` | `{ id, name, projectRoot }` | A workspace is created. This is the only current global event. |
5152
5376
  | `workspace/preparation` | `{ workspaceId, preparation: FunctionalityPreparationActivity[] }` | Workspace capability preparation changes; snapshot and clearing semantics follow {§functionality-preparation-visibility}. |
5153
- | §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. |
5154
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. |
5155
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. |
5156
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. |
@@ -5173,21 +5397,66 @@ the authoritative scope and is `null` only for a global event. Core does not
5173
5397
  mutate each payload to repeat it. A transport module stamps that scope onto any
5174
5398
  outward envelope that requires it and owns workspace fan-out.
5175
5399
 
5176
- ### §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.
5177
5407
 
5178
- A module client is an actor ({§machine-processes}). Its dispatched side effects
5179
- write to its own client worker with `origin="client"` and execute in the
5180
- workspace's Functionality
5181
- ({§actor-boundary-attached-functionality}); one client action owns
5182
- one administrative loop, and its statements become ordered operation turns
5183
- inside that loop. A proposal may hold its turn and loop across an external
5184
- interrupt/resume, but those records preserve durable evidence rather than
5185
- defining the public client lifecycle. Multiple client actors have distinct workers.
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.
5186
5414
 
5187
- `runLoop` targets a separate model worker holding the conversation with
5188
- `origin="model"`. Both workers share workspace state, while a packet renders
5189
- only the model worker's private log; client action rows are structurally absent
5190
- without an origin filter ({§actor-boundary-isolation}).
5415
+ ### §connection-lifecycle Client operations
5416
+
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}) |
5191
5460
 
5192
5461
  ---
5193
5462
 
@@ -5261,7 +5530,7 @@ Conditional absence never reorders the surviving default sections.
5261
5530
  | 6 | user | `git` | Per-turn workspace status; empty content is omitted. |
5262
5531
  | 7 | user | `budget` | `Context Curation`; omitted when capacity is unknown. |
5263
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}). |
5264
- | 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}). |
5265
5534
  | 10 | user | `messages` | `Open Messages`: immutable message addresses and causal sources ({§message-arrival}). |
5266
5535
  | 11 | user | `notices` | Per-turn observations; empty content is omitted. |
5267
5536
  | 12 | user | `errors` | Per-turn failure pointers; empty content is omitted. |
@@ -5271,7 +5540,7 @@ The order favors prefix-cache locality where semantics permit: the definition
5271
5540
  and privileged policy lead operator notes, while the append-mostly
5272
5541
  log and interpolated programs lead the volatile user-status clump. That clump is never cached
5273
5542
  across turns, since the next turn's log grows into its place, so its order serves attention alone:
5274
- 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
5275
5544
  Messages) to feedback on the last turn (Notices, then Errors), and closes with the Recap. The nearer
5276
5545
  the end, the more a section asks of this turn. With `all`, appending new
5277
5546
  observations and programs preserves the existing history's roles and text prefix; no old program
@@ -5332,8 +5601,7 @@ flowchart TD
5332
5601
  ```
5333
5602
 
5334
5603
  - §context-budget **One room.** The budget is the provider's input capacity through the loop's calibration, `floor(inputCapacity / factor)` ({§tokenomics-calibrated-readout}), and it bounds the whole packet — teaching, catalog, log and gauge together. Nothing on the service panel sizes a part of it: no projection share or preview page. When the loop's first response fixes its factor, the budget follows it either way; a room that shrinks under the model is announced by the over-budget row ({§context-over-budget-row}), never hidden by keeping a larger figure.
5335
- - §context-gauge **One gauge.** Every packet carries one `## Context` section holding one JSON object: `tokens`, the packet's weight as rendered, and `budget`, the known room. Only when the packet without an inventory strictly exceeds `PLURNK_SERVICE_BUDGET_PRESSURE` of its budget does `largest` name the `PLURNK_SERVICE_BUDGET_LARGEST_ITEMS` heaviest retained log rows as `{path, tokens}`, ranked by weight then path. Unknown budget omits both `budget` and `largest`. The final `tokens` includes the inventory; over budget, its full configured count remains available for curation ({§context-over-budget-row}). Each log row heading carries its own `tokens`. The gauge is state: it never presents `budget − tokens` as response allowance and carries no mandate.
5336
- - §context-pressure-notice **Pressure is one notice.** When the gauge names its inventory ({§context-gauge}) and the packet, with the notice in it, stays within its budget, the packet's notices section carries one `engine:context` notice `budget_pressure`, `level: warn`, whose directive is "YOU MUST NOT exceed budget." and whose message is "Context is at *N*% of budget." *N* is `floor(100 × tokens / budget)` over the gauge's final figures, so the share never disagrees with the gauge, and the notice carries no count of its own. It is decided and rendered with the packet it describes, recurs on every such packet, and is never buffered or sent to clients, which read their own pair ({§tokenomics-client-gauge}). An over-budget packet carries the row instead ({§context-over-budget-row}).
5604
+ - §context-gauge **One gauge.** Every packet carries one `## Context` section holding one JSON object: `tokens`, the packet's weight as rendered, `budget`, the known room, and `percent`, `floor(100 × tokens / budget)` over the gauge's own final figures, so the share never disagrees with the counts beside it; a zero budget states no share. Only when the packet without an inventory strictly exceeds `PLURNK_SERVICE_BUDGET_PRESSURE` of its budget does `largest` name the `PLURNK_SERVICE_BUDGET_LARGEST_ITEMS` heaviest retained log rows as `{path, tokens}`, ranked by weight then path. Unknown budget omits `budget`, `percent` and `largest`. The final `tokens` includes the inventory; over budget, its full configured count remains available for curation ({§context-over-budget-row}). Each log row heading carries its own `tokens`. The gauge is state: it never presents `budget − tokens` as response allowance and carries no mandate.
5337
5605
  - §context-fit **One rule: whole if it fits, otherwise as much as fits, then its size and address.** A result the model asked for — a READ without a scope, a FIND, an exec result delivered on close — arrives complete when its weight fits within `budget − tokens` as of that turn, less the room the turn still owes — one receipt's measured weight for each result still to land in it and the emission record that answers it, so that the receipts of a batch and the answer always fit. Otherwise the complete result stays where it was read from and the row is a `413` receipt naming the result's size, its weight, the remaining budget, and the two verbs: `READ` a range, or `KILL` first. A READ of lines carries, above its receipt, the longest prefix of those lines whose row fits that room — the lines with their anchors, the returned range closed on the last line delivered, and the receipt naming the lines delivered; a result whose first line does not fit, a FIND, a stream page and a byte view are bodiless receipts. An arrival the model did not ask for — an inbound `SEND`, a child's conclusion — takes the same bodiless receipt as a fact, not an error: its size and its address. Explicit scopes (`<a,b>`, a pattern, `<1,-1>`) are exact and take the same test. Result cuts are named where they happen: the first page of what came back unasked in size ({§markerless-first-page}) and the cut at the room in the receipt. The optional emission history is whole or absent ({§emission-history}), never a cut.
5338
5606
  - §context-verbs **The verbs are the curation.** `READ` with a range takes a piece of what did not fit ({§read-selection-projection}); `NOTE` keeps what the model learned; `KILL` retires rows, ranges and bodies ({§log-kill-scope}) and may carry what it keeps of them as its own NOTE ({§log-kill-distillation}). The daemon adds no other mechanism.
5339
5607
  - §context-over-budget-row **Over budget is a row and a request, not an ending.** A packet whose weight exceeds `budget` is still submitted when it fits the window ({§context-wall}), and it carries one `_plurnk` error row at the head of its turn, `engine/context/packet-exceeds-budget` 413: "Context exceeds budget. YOU MUST ONLY KILL, MOVE or NOTE this turn." The row is the mandate and carries no figure: the gauge is the one home for `tokens` and `budget` ({§context-gauge}), and it shows the packet over with the row in it. The row is shown once, in the packet it describes, then retired from the projection as a no-match KILL receipt is ({§log-kill-meta-operation}): it stays durable and readable at its coordinate, and a later packet that is over carries its own row. Nothing is hidden to make the packet smaller; the response grant is the window's remainder above the provider's output floor ({§provider-output-floor}). The limit protects a loop; it never ends one.
@@ -5397,7 +5665,7 @@ flowchart LR
5397
5665
  log --> packet["Packet lists coordinate;<br/>READ recalls exact body"]
5398
5666
  ```
5399
5667
 
5400
- §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
5401
5669
  pre-turn, a worker materializes only occurrences whose structural audience
5402
5670
  includes that worker. The set is exhaustive, unranked, and exactly once; the
5403
5671
  engine makes no relevance decision. Each copied event retains the operation,
@@ -5591,9 +5859,9 @@ another admission is a 409 conflict, not a second message or an implicit content
5591
5859
 
5592
5860
  | Audience | Delivery | Effect |
5593
5861
  |---|---|---|
5594
- | 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. |
5595
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. |
5596
- | 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. |
5597
5865
  | Replying actor | Its own executed SEND | No duplicate ambient occurrence. |
5598
5866
 
5599
5867
  The successful SEND and its addressed occurrences commit together. Reply occurrences use
@@ -5624,7 +5892,9 @@ published, oldest first, and stamps each with the row it became. Ordinal 1 is th
5624
5892
  own assignment and shares the loop's fate: a loop that fails before its first turn does
5625
5893
  not replay it. Every other still-unpublished message at conclusion moves into one
5626
5894
  source-keyed recovery loop, renumbered from its first, whose headline it becomes; that loop's first turn publishes the complete
5627
- 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
5628
5898
  mint duplicate work. Output withholding preserves readable arrival rows; explicit
5629
5899
  KILL follows the ordinary log contract.
5630
5900
 
@@ -5690,10 +5960,9 @@ retain distinct contracts and lifetimes.
5690
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 |
5691
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 |
5692
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 |
5693
- | `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 |
5694
- | `budget_pressure` | engine context — a packet within its budget whose gauge is under pressure ({§context-pressure-notice}); `level: warn`; rendered with that packet, never buffered | YOU MUST NOT exceed budget. | 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 |
5695
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 |
5696
- | `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 |
5697
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 |
5698
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 |
5699
5968
  | `provider_retry` | a provider stream that failed after output began and was answered by a complete retry; `level: warn` | none | none |
@@ -5705,13 +5974,13 @@ retain distinct contracts and lifetimes.
5705
5974
  | `scope_normalized` | engine slicer — a scope normalized to its canonical form; `level: warn` | YOU SHOULD write the normalized scope. | none |
5706
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 |
5707
5976
 
5708
- §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.
5709
5978
 
5710
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:
5711
5980
 
5712
5981
  ```text
5713
5982
  > [!WARNING]
5714
- > 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]
5715
5984
  ```
5716
5985
 
5717
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.
@@ -5721,7 +5990,10 @@ retain distinct contracts and lifetimes.
5721
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.
5722
5991
 
5723
5992
  §loop-status-notice **The drain publishes lifecycle and its observation deadline together.**
5724
- `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.
5725
5997
 
5726
5998
  | Edge | `status` | `waitUntil` |
5727
5999
  |---|---|---|
@@ -5743,7 +6015,7 @@ the same deadline through {§application-loop-observation}, not a restarted cloc
5743
6015
 
5744
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.
5745
6017
 
5746
- §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.
5747
6019
 
5748
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}).
5749
6021
 
@@ -5831,15 +6103,18 @@ remain one compact orientation line, not an expansion of every tool's signature.
5831
6103
 
5832
6104
  §members-functionality **File membership is one workspace Functionality family.**
5833
6105
  Core registers the `members` family with the coordinator ({§functionality-coordinator}):
5834
- the model, the client, and the operator learn one surface — `list | discover | add | replace |
6106
+ the model, the client, and the operator learn one surface — `list | discover | add |
5835
6107
  enable | disable | remove`, `workspace.members.<verb>` for the client,
5836
6108
  ```` ```members (<verb>) ```` for the model — for what the model may see, exactly as they do for skills and
5837
- MCP servers. A definition is one shell-path glob ({§path-glob-matching}), `{ glob }`, relative to the project
5838
- root; a leading `!` excludes matching members, and an exclusion wins over every inclusion.
5839
- Admission provenance (`service-configuration`, `client-action`, `model-proposal`)
5840
- rides the definition to enforce {§members-model-scope}; configuration-source
5841
- provenance belongs to the shared inspection projection ({§configuration-provenance}).
5842
- The alias is a short name, suggested from the glob. `list` shows each
6109
+ MCP servers. A definition is one shell-path glob ({§path-glob-matching}) relative to the project
6110
+ root; a leading `!` excludes matching members, and an exclusion wins over every inclusion. The glob
6111
+ is the definition's identity ({§functionality-addressing}): `add` takes `{"files": ["<glob>", …]}`
6112
+ and admits each glob, trimmed, as one definition, a glob named twice being one; `enable`, `disable`
6113
+ and `remove` take `{"file": "<glob>"}`, the glob as `list` shows it. A bare `!` is 400
6114
+ `definition-invalid`. Admission provenance (`service-configuration`, `client-action`,
6115
+ `model-proposal`) rides the persisted definition, `{ glob, provenance }`, to enforce
6116
+ {§members-model-scope}; configuration-source provenance belongs to the shared inspection projection
6117
+ ({§configuration-provenance}). `list` shows each
5843
6118
  definition with what it resolved to — `include` or `exclude`, the pattern, the members it
5844
6119
  admits or removes (count and a bounded sample), and for a model's inclusion the matches the
5845
6120
  repository's ignore rules refused — so the model sees what its glob did and adapts.
@@ -5850,7 +6125,9 @@ or exclude. A query that names neither, blank or a bare `!`, is 400 `query-inval
5850
6125
  never content; nothing is added.
5851
6126
 
5852
6127
  §members-configuration *Available definitions.* `PLURNK_MEMBERS_<alias>=<glob>`
5853
- declares one service-origin rule (`!glob` excludes). The shared naming and
6128
+ declares one service-origin rule (`!glob` excludes), named by its glob like every definition; the
6129
+ key is its configuration provenance, which `list` shows, and two keys declaring one glob fail by
6130
+ name. The shared naming and
5854
6131
  enabledness dialect is {§resource-environment}; `PLURNK_MEMBERS_ENABLED` supplies
5855
6132
  the panel default and `PLURNK_MEMBERS_<alias>_ENABLED` overrides one rule.
5856
6133
  An empty glob or a bare `!` fails validation. Controls may precede their rule;
@@ -5877,7 +6154,8 @@ the repository ignores ({§membership-model-universe}). The engine's creation re
5877
6154
  overwrites or retires them. Projection happens at the family's publication commit and
5878
6155
  re-resolves membership; workspace cooling changes nothing, because desired state is
5879
6156
  durable. Each enabled definition is one generated document at
5880
- `worker:///_plurnk/members/<alias>.md` ({§functionality-documents}) — its glob, origin,
6157
+ `worker:///_plurnk/members/<glob>.md` ({§functionality-documents}), the glob percent-encoded as
6158
+ one path segment (RFC 3986 unreserved characters and parentheses stay literal) — its glob, origin,
5881
6159
  provenance, and what it resolved to — surveyed at turn 0 like every family's enabled
5882
6160
  definitions ({§actor-boundary-catalog-preview}), so the model sees why a file is or is
5883
6161
  not a member before it asks. There is no other membership path: the client's `/members`
@@ -6309,7 +6587,25 @@ lane's run directory and the lane's reclaim takes all of it; an unnamed run is C
6309
6587
  that passes is reclaimed when it exits; a failed suite's evidence is never touched and stays exactly where the run reported it. A cross-package test may reuse Core's migration fixture only by passing a path inside the
6310
6588
  caller's own run directory.
6311
6589
 
6312
- §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.
6590
+ §tier-census **A real-model tier run is one directory with its census.** The live and demo drivers
6591
+ stamp `PLURNK_TIER_RUN` as `<kind>-<UTC stamp>`; every worker the run claims lands inside
6592
+ `PLURNK_BENCHMARKS/tier-<kind>-<stamp>/`, each in its own `<label>-XXXXXX/` with its database, its own
6593
+ host, its digest and a `census.json` naming the specimen that drove it, its loops, turns, requests,
6594
+ tokens and cost. The run's `census.md` and `census.json`, written by the tier's node:test reporter
6595
+ when the run ends, carry one row per specimen: verdict, skip reason or first failure line, the
6596
+ workers it claimed, turns, requests and cost; a run never deletes or moves any of it. A specimen
6597
+ declares what it requires of the route and the host (`input:<image|pdf|audio|video>`, `reasoning`,
6598
+ `output-budget`, `env:<NAME>`, `url:<http(s) URL>`, `bin:<name>` on PATH, `checkout:<client|a2a-tck>`
6599
+ at `PLURNK_CLIENT_CHECKOUT` or `PLURNK_A2A_TCK_CHECKOUT`, else beside the repository); an unmet
6600
+ requirement is a skip, before anything boots or is spent, whose reason names the requirement and what
6601
+ was observed, and the census answers each unmet route-bound requirement with the declared aliases
6602
+ whose catalog entry satisfies it. A route the harness cannot resolve is a failure, never a skip. No
6603
+ test surface is an opt-in flag or a script of its own: the third-party MCP composition, the
6604
+ llama-server rails, the upstream A2A TCK and the client's real-model TUI tier are live specimens that
6605
+ run wherever their requirements hold. `tier:history` ledgers every census under the home: per
6606
+ specimen, its verdicts over runs and its last reason.
6607
+
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.
6313
6609
 
6314
6610
  ## Problem codes and pinned wording
6315
6611
 
@@ -6341,11 +6637,15 @@ Every Problem code core mints is named here under its family ({§problem-error-c
6341
6637
  | `kill-target-scheme-required` | 400 | KILL target requires a scheme. |
6342
6638
  | `worker-not-found` | 404 | Worker '*name*' does not exist in this workspace. |
6343
6639
  | `entry-operation-unsupported` | 400 | KILL requires an entry-bearing target; '*scheme*' does not provide one. |
6640
+ | `coordinate-invalid` | 400 | '*to*' is not a log coordinate ({§rollback}). Recovery: Name the coordinate as `<loop>/<turn>/<item>`, as the log shows it. |
6641
+ | `coordinate-not-found` | 404 | No log entry exists at log:///*coordinate* ({§rollback}). |
6642
+ | `project-root-required` | 400 | Rollback restores files, and this workspace has no project root ({§rollback}). |
6344
6643
  | `resource-scheme-required` | 400 | Resource selection requires an address. |
6345
6644
  | `channel-required` | 400 | The '*scheme*' scheme has no default channel. Recovery: Address a named channel with a URI fragment. |
6346
6645
  | `binary-source-unsupported` | 415 | Channel #*name* is binary and its scheme keeps no bytes to transfer. |
6347
- | `metadata-unsupported` | 400 | *OP* takes only the env option; '*key*' is not one. |
6348
- | `worker-name-conflict` | 409 | Worker '*name*' already exists in this workspace. |
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}). |
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}) |
6349
6649
  | `no-operation` | 422 | The turn performed no operation ({§empty-turn}). |
6350
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. |
6351
6651
  | `invalid-operation-syntax` | 400 | The parser's diagnostic for an invalid operation in an admitted turn. |
@@ -6408,7 +6708,7 @@ lists the schemes the workspace registers, and rebuilds the address into nothing
6408
6708
  | `file-create-excluded` | 403 | A members exclusion (`!_glob_`) covers '*path*'. Recovery: Remove or disable the excluding members definition, or choose another path. |
6409
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. |
6410
6710
  | `file-materialization-limit` | 413 | The file exceeds the materialization byte limit and is not read into the workspace. |
6411
- | `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 `{"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. |
6412
6712
  | `body-channel-required` | 400 | A file write requires a body channel. |
6413
6713
  | `write-failed` | 500 | Publication or its settlement failed; the result distinguishes an unpublished write from an already-published one. |
6414
6714
  | `creation-rollback-failed` | 500 | Incorporating a newly created file failed, and rollback was incomplete. |
@@ -6421,7 +6721,7 @@ lists the schemes the workspace registers, and rebuilds the address into nothing
6421
6721
  | code | status | contract |
6422
6722
  |---|---:|---|
6423
6723
  | `invalid-input-target` | 400 | SEND input addresses an execution, without a channel or scope. |
6424
- | `input-unavailable` | 409 | This execution's input receiver is no longer enabled. |
6724
+ | `input-unavailable` | 409 | This execution was launched without input. Recovery: Launch it with `[{"stdin": "open"}]` when its runtime takes input, then SEND to that execution. — This execution is queued for an execution slot; its input opens when it starts. Recovery: SEND again once the execution has started. — This execution's input receiver is no longer enabled. |
6425
6725
  | `stream-not-found` | 404 | No execution exists at the requested address. Recovery, when the id holds an execution under another runtime ({§exec-near-miss}): The execution at /*id* is `*runtime*:///*id*`. |
6426
6726
  | `input-closed` | 410 | Execution input is closed. |
6427
6727
  | `executor-not-registered` | 400 | No executor or MCP service named '*runtime*' is registered for this worker. |
@@ -6444,6 +6744,7 @@ lists the schemes the workspace registers, and rebuilds the address into nothing
6444
6744
  |---|---:|---|
6445
6745
  | `read-target-required` | 400 | READ requires a log coordinate. Recovery: Provide one exact log coordinate. |
6446
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}). |
6447
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. |
6448
6749
  | `curation-failed` | 400 | A scoped KILL's selection could not be read: the same sentences and recovery as `kill-failed`. |
6449
6750
  | `worker-target-required` | 400 | EDIT requires a worker:// target. Recovery: Provide the worker target. |
@@ -6464,14 +6765,16 @@ lists the schemes the workspace registers, and rebuilds the address into nothing
6464
6765
  | code | status | condition |
6465
6766
  |---|---:|---|
6466
6767
  | `interaction-not-pending` | 409 | The requested client interaction is not pending. |
6467
- | `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. |
6468
6769
  | `recipient-mismatch` | 403 | Only the interaction's recipient may answer it. |
6469
6770
  | `interaction-response-invalid` | 400 | The response does not satisfy the pending interaction's schema. |
6470
6771
  | `invalid-body` | 400 | A question body is not JSON, or lacks its nonempty message or requested schema. |
6471
6772
  | `offset-invalid` | 400 | An entry offset is not a nonnegative safe integer. |
6472
6773
  | `userinfo-not-allowed` | 400 | An entry target URL contains userinfo. |
6473
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}). |
6474
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. |
6475
6778
  | `source-not-running` | 409 | The originating task is not running; no message was admitted. |
6476
6779
  | `loop-not-open` | 409 | The named loop is not an unfinished recipient of the worker; no message was admitted. |
6477
6780
 
@@ -6518,18 +6821,18 @@ lists the schemes the workspace registers, and rebuilds the address into nothing
6518
6821
  | Nothing is in flight. Continuing. | a WAIT (or a premature terminal) with no live work ({§wait-obligation-matrix}) |
6519
6822
  | All Open Messages resolved. Review new results; emit no OPs if finished. | resolved messages with unobserved results ({§loop-completion}) |
6520
6823
  | Context exceeds budget. YOU MUST ONLY KILL, MOVE or NOTE this turn. | the over-budget row ({§context-over-budget-row}) |
6521
- | YOU MUST NOT exceed budget. Context is at *N*% of budget. | a packet within its budget under pressure ({§context-pressure-notice}) |
6522
- | 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}) |
6523
6825
  | Each directive in the notice table | a notice of the model's own output ({§notice-directive}) |
6524
6826
  | Context window overflow: the packet cannot fit the model's window even as receipts. | the wall ({§context-wall}) |
6525
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}). |
6526
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}) |
6527
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 |
6528
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}) |
6529
6832
  | The stream was cancelled by KILL. | a stream terminal after KILL |
6530
6833
  | '*runtime*' exited with code 127; `*program*` is a registered tool of `*owner*`. | a tool run as a shell command ({§exec-tool-fall-through}) |
6531
6834
  | '*path*' is a directory, not a file; READ reads one file. Recovery: `FIND (_path_/)` lists its files. | READ of a directory |
6532
6835
  | The execution at ops://*worker*/*loop* has not concluded. | a bare READ of a running worker's result (425) |
6533
6836
  | The child provider failed. | a child's provider failure read back by its parent |
6534
- | '*path*' exists on disk but is not a member of this workspace. Recovery: Admit it with `members (add)` and a `{"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 |
6535
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}) |