@plurnk/plurnk-service 1.29.0 → 3.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 (385) hide show
  1. package/.env.defaults +14 -33
  2. package/INSTALL.md +68 -17
  3. package/README.md +9 -7
  4. package/SPEC.md +503 -741
  5. package/dist/build-info.json +1 -1
  6. package/dist/content/body-preview.d.ts.map +1 -1
  7. package/dist/content/body-preview.js +11 -2
  8. package/dist/content/body-preview.js.map +1 -1
  9. package/dist/content/edit-receipt.d.ts +1 -1
  10. package/dist/content/edit-receipt.d.ts.map +1 -1
  11. package/dist/content/edit-receipt.js +1 -1
  12. package/dist/content/edit-receipt.js.map +1 -1
  13. package/dist/content/line-marker.js +1 -1
  14. package/dist/content/line-marker.js.map +1 -1
  15. package/dist/content/path-mimetype.js +1 -1
  16. package/dist/content/path-mimetype.js.map +1 -1
  17. package/dist/content/read-projector.d.ts.map +1 -1
  18. package/dist/content/read-projector.js +3 -12
  19. package/dist/content/read-projector.js.map +1 -1
  20. package/dist/core/AdministrativeLoop.d.ts +1 -1
  21. package/dist/core/AdministrativeLoop.d.ts.map +1 -1
  22. package/dist/core/AdministrativeLoop.js +2 -6
  23. package/dist/core/AdministrativeLoop.js.map +1 -1
  24. package/dist/core/AdmittedTurnExecutor.d.ts +4 -3
  25. package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
  26. package/dist/core/AdmittedTurnExecutor.js +19 -17
  27. package/dist/core/AdmittedTurnExecutor.js.map +1 -1
  28. package/dist/core/BareBatchRunner.d.ts +2 -2
  29. package/dist/core/BareBatchRunner.d.ts.map +1 -1
  30. package/dist/core/BareBatchRunner.js.map +1 -1
  31. package/dist/core/BudgetReadout.d.ts +1 -0
  32. package/dist/core/BudgetReadout.d.ts.map +1 -1
  33. package/dist/core/BudgetReadout.js +12 -0
  34. package/dist/core/BudgetReadout.js.map +1 -1
  35. package/dist/core/CapabilityPolicies.d.ts +2 -4
  36. package/dist/core/CapabilityPolicies.d.ts.map +1 -1
  37. package/dist/core/CapabilityPolicies.js +3 -23
  38. package/dist/core/CapabilityPolicies.js.map +1 -1
  39. package/dist/core/CapabilityResolver.d.ts +9 -2
  40. package/dist/core/CapabilityResolver.d.ts.map +1 -1
  41. package/dist/core/CapabilityResolver.js +34 -5
  42. package/dist/core/CapabilityResolver.js.map +1 -1
  43. package/dist/core/ChannelWrite.d.ts +0 -2
  44. package/dist/core/ChannelWrite.d.ts.map +1 -1
  45. package/dist/core/ChannelWrite.js.map +1 -1
  46. package/dist/core/ClientInteractions.d.ts +3 -2
  47. package/dist/core/ClientInteractions.d.ts.map +1 -1
  48. package/dist/core/ClientInteractions.js +21 -12
  49. package/dist/core/ClientInteractions.js.map +1 -1
  50. package/dist/core/ClientInteractions.sql +8 -2
  51. package/dist/core/CoreSchemeServices.d.ts +1 -0
  52. package/dist/core/CoreSchemeServices.d.ts.map +1 -1
  53. package/dist/core/CoreSchemeServices.js +1 -0
  54. package/dist/core/CoreSchemeServices.js.map +1 -1
  55. package/dist/core/DataStatementRunner.js +1 -1
  56. package/dist/core/DataStatementRunner.js.map +1 -1
  57. package/dist/core/Dispatcher.d.ts +3 -2
  58. package/dist/core/Dispatcher.d.ts.map +1 -1
  59. package/dist/core/Dispatcher.js +15 -21
  60. package/dist/core/Dispatcher.js.map +1 -1
  61. package/dist/core/Engine.d.ts +2 -2
  62. package/dist/core/Engine.d.ts.map +1 -1
  63. package/dist/core/Engine.js +4 -3
  64. package/dist/core/Engine.js.map +1 -1
  65. package/dist/core/EnvFlags.d.ts.map +1 -1
  66. package/dist/core/EnvFlags.js +4 -3
  67. package/dist/core/EnvFlags.js.map +1 -1
  68. package/dist/core/ExecutorRegistry.d.ts +3 -17
  69. package/dist/core/ExecutorRegistry.d.ts.map +1 -1
  70. package/dist/core/ExecutorRegistry.js +6 -6
  71. package/dist/core/ExecutorRegistry.js.map +1 -1
  72. package/dist/core/FabricatedLog.js +1 -1
  73. package/dist/core/FabricatedLog.js.map +1 -1
  74. package/dist/core/HostPaths.d.ts +2 -1
  75. package/dist/core/HostPaths.d.ts.map +1 -1
  76. package/dist/core/HostPaths.js +14 -2
  77. package/dist/core/HostPaths.js.map +1 -1
  78. package/dist/core/KillHandler.js +1 -1
  79. package/dist/core/KillHandler.js.map +1 -1
  80. package/dist/core/LogVisibility.d.ts.map +1 -1
  81. package/dist/core/LogVisibility.js +8 -17
  82. package/dist/core/LogVisibility.js.map +1 -1
  83. package/dist/core/LoopDriver.d.ts.map +1 -1
  84. package/dist/core/LoopDriver.js +4 -6
  85. package/dist/core/LoopDriver.js.map +1 -1
  86. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  87. package/dist/core/LoopLifecycle.js +4 -9
  88. package/dist/core/LoopLifecycle.js.map +1 -1
  89. package/dist/core/LoopLifecycle.sql +6 -6
  90. package/dist/core/OperatorConfig.d.ts +1 -0
  91. package/dist/core/OperatorConfig.d.ts.map +1 -1
  92. package/dist/core/OperatorConfig.js +10 -2
  93. package/dist/core/OperatorConfig.js.map +1 -1
  94. package/dist/core/PacketBuilder.d.ts +5 -7
  95. package/dist/core/PacketBuilder.d.ts.map +1 -1
  96. package/dist/core/PacketBuilder.js +49 -119
  97. package/dist/core/PacketBuilder.js.map +1 -1
  98. package/dist/core/PacketBuilder.sql +15 -0
  99. package/dist/core/ProposalLifecycle.d.ts +1 -1
  100. package/dist/core/ProposalLifecycle.d.ts.map +1 -1
  101. package/dist/core/ProposalLifecycle.js +18 -20
  102. package/dist/core/ProposalLifecycle.js.map +1 -1
  103. package/dist/core/ProposalPolicies.d.ts +6 -0
  104. package/dist/core/ProposalPolicies.d.ts.map +1 -0
  105. package/dist/core/ProposalPolicies.js +28 -0
  106. package/dist/core/ProposalPolicies.js.map +1 -0
  107. package/dist/core/ProviderInstantiate.d.ts.map +1 -1
  108. package/dist/core/ProviderInstantiate.js +3 -4
  109. package/dist/core/ProviderInstantiate.js.map +1 -1
  110. package/dist/core/ResourceSelector.js +2 -2
  111. package/dist/core/ResourceSelector.js.map +1 -1
  112. package/dist/core/ResourceTransfers.js +3 -3
  113. package/dist/core/ResourceTransfers.js.map +1 -1
  114. package/dist/core/RuntimeWorker.sql +1 -2
  115. package/dist/core/SchemeRegistry.d.ts +5 -4
  116. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  117. package/dist/core/SchemeRegistry.js +11 -12
  118. package/dist/core/SchemeRegistry.js.map +1 -1
  119. package/dist/core/ToolInputSchema.d.ts +1 -0
  120. package/dist/core/ToolInputSchema.d.ts.map +1 -1
  121. package/dist/core/ToolInputSchema.js +8 -2
  122. package/dist/core/ToolInputSchema.js.map +1 -1
  123. package/dist/core/ToolResources.d.ts +1 -0
  124. package/dist/core/ToolResources.d.ts.map +1 -1
  125. package/dist/core/ToolResources.js +12 -6
  126. package/dist/core/ToolResources.js.map +1 -1
  127. package/dist/core/Turn.js +1 -1
  128. package/dist/core/Turn.js.map +1 -1
  129. package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
  130. package/dist/core/TurnDispositionHandler.js +1 -7
  131. package/dist/core/TurnDispositionHandler.js.map +1 -1
  132. package/dist/core/TurnMaterialization.js +1 -1
  133. package/dist/core/TurnMaterialization.js.map +1 -1
  134. package/dist/core/TurnOps.d.ts +1 -1
  135. package/dist/core/TurnOps.d.ts.map +1 -1
  136. package/dist/core/TurnOps.js +4 -12
  137. package/dist/core/TurnOps.js.map +1 -1
  138. package/dist/core/TurnRunner.d.ts +1 -2
  139. package/dist/core/TurnRunner.d.ts.map +1 -1
  140. package/dist/core/TurnRunner.js +85 -81
  141. package/dist/core/TurnRunner.js.map +1 -1
  142. package/dist/core/WorkerControlHandler.d.ts.map +1 -1
  143. package/dist/core/WorkerControlHandler.js +1 -8
  144. package/dist/core/WorkerControlHandler.js.map +1 -1
  145. package/dist/core/WorkerName.d.ts +1 -0
  146. package/dist/core/WorkerName.d.ts.map +1 -1
  147. package/dist/core/WorkerName.js +2 -0
  148. package/dist/core/WorkerName.js.map +1 -1
  149. package/dist/core/WorkerName.sql +2 -2
  150. package/dist/core/WorkerOwners.d.ts +13 -0
  151. package/dist/core/WorkerOwners.d.ts.map +1 -0
  152. package/dist/core/WorkerOwners.js +44 -0
  153. package/dist/core/WorkerOwners.js.map +1 -0
  154. package/dist/core/WorkerOwners.sql +55 -0
  155. package/dist/core/caps/DbNotifyCaps.js +2 -2
  156. package/dist/core/caps/DbNotifyCaps.js.map +1 -1
  157. package/dist/core/caps/DbProjectionCaps.js +1 -1
  158. package/dist/core/caps/DbProjectionCaps.js.map +1 -1
  159. package/dist/core/env-catalog.d.ts +3 -0
  160. package/dist/core/env-catalog.d.ts.map +1 -1
  161. package/dist/core/env-catalog.js +44 -9
  162. package/dist/core/env-catalog.js.map +1 -1
  163. package/dist/core/env-defaults.d.ts +2 -1
  164. package/dist/core/env-defaults.d.ts.map +1 -1
  165. package/dist/core/env-defaults.js +17 -9
  166. package/dist/core/env-defaults.js.map +1 -1
  167. package/dist/core/fork.sql +3 -4
  168. package/dist/core/packet-inject.js +0 -2
  169. package/dist/core/packet-inject.js.map +1 -1
  170. package/dist/core/packet-wire.d.ts +2 -11
  171. package/dist/core/packet-wire.d.ts.map +1 -1
  172. package/dist/core/packet-wire.js +31 -111
  173. package/dist/core/packet-wire.js.map +1 -1
  174. package/dist/core/plurnk-uri.d.ts +1 -1
  175. package/dist/core/plurnk-uri.d.ts.map +1 -1
  176. package/dist/core/plurnk-uri.js +4 -4
  177. package/dist/core/plurnk-uri.js.map +1 -1
  178. package/dist/core/scheme-types.d.ts +2 -1
  179. package/dist/core/scheme-types.d.ts.map +1 -1
  180. package/dist/core/types.d.ts +0 -1
  181. package/dist/core/types.d.ts.map +1 -1
  182. package/dist/core/unregistered-scheme.d.ts +1 -3
  183. package/dist/core/unregistered-scheme.d.ts.map +1 -1
  184. package/dist/core/unregistered-scheme.js +3 -11
  185. package/dist/core/unregistered-scheme.js.map +1 -1
  186. package/dist/evidence/EvidenceReader.d.ts +14 -0
  187. package/dist/evidence/EvidenceReader.d.ts.map +1 -0
  188. package/dist/evidence/EvidenceReader.js +137 -0
  189. package/dist/evidence/EvidenceReader.js.map +1 -0
  190. package/dist/{digest → evidence}/digest.sql +1 -2
  191. package/dist/schemes/EffectPolicy.d.ts.map +1 -1
  192. package/dist/schemes/EffectPolicy.js +1 -10
  193. package/dist/schemes/EffectPolicy.js.map +1 -1
  194. package/dist/schemes/Exec.d.ts.map +1 -1
  195. package/dist/schemes/Exec.js +19 -26
  196. package/dist/schemes/Exec.js.map +1 -1
  197. package/dist/schemes/ExecOutputScheme.d.ts +1 -1
  198. package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
  199. package/dist/schemes/File.d.ts.map +1 -1
  200. package/dist/schemes/File.js +11 -17
  201. package/dist/schemes/File.js.map +1 -1
  202. package/dist/schemes/Log.d.ts.map +1 -1
  203. package/dist/schemes/Log.js +14 -11
  204. package/dist/schemes/Log.js.map +1 -1
  205. package/dist/schemes/QuestionTool.d.ts.map +1 -1
  206. package/dist/schemes/QuestionTool.js +1 -3
  207. package/dist/schemes/QuestionTool.js.map +1 -1
  208. package/dist/schemes/{Skill.d.ts → ResourceTreeScheme.d.ts} +5 -6
  209. package/dist/schemes/ResourceTreeScheme.d.ts.map +1 -0
  210. package/dist/schemes/{Skill.js → ResourceTreeScheme.js} +63 -43
  211. package/dist/schemes/ResourceTreeScheme.js.map +1 -0
  212. package/dist/schemes/Worker.d.ts.map +1 -1
  213. package/dist/schemes/Worker.js +7 -9
  214. package/dist/schemes/Worker.js.map +1 -1
  215. package/dist/schemes/_entry-crud.sql +1 -1
  216. package/dist/schemes/_entry-find.js +2 -2
  217. package/dist/schemes/_entry-find.js.map +1 -1
  218. package/dist/schemes/_entry-find.sql +1 -1
  219. package/dist/schemes/_entry-fts.d.ts.map +1 -1
  220. package/dist/schemes/_entry-fts.js +3 -9
  221. package/dist/schemes/_entry-fts.js.map +1 -1
  222. package/dist/schemes/_entry-manifest.js +1 -1
  223. package/dist/schemes/_entry-manifest.js.map +1 -1
  224. package/dist/schemes/_entry-ops.js +1 -1
  225. package/dist/schemes/_entry-ops.js.map +1 -1
  226. package/dist/server/AgentRoots.d.ts +2 -1
  227. package/dist/server/AgentRoots.d.ts.map +1 -1
  228. package/dist/server/AgentRoots.js +13 -10
  229. package/dist/server/AgentRoots.js.map +1 -1
  230. package/dist/server/ConfigurationDiagnostics.d.ts +5 -3
  231. package/dist/server/ConfigurationDiagnostics.d.ts.map +1 -1
  232. package/dist/server/ConfigurationDiagnostics.js +24 -8
  233. package/dist/server/ConfigurationDiagnostics.js.map +1 -1
  234. package/dist/server/Daemon.d.ts +25 -16
  235. package/dist/server/Daemon.d.ts.map +1 -1
  236. package/dist/server/Daemon.js +137 -96
  237. package/dist/server/Daemon.js.map +1 -1
  238. package/dist/server/DrainSupervisor.d.ts +1 -4
  239. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  240. package/dist/server/DrainSupervisor.js +1 -11
  241. package/dist/server/DrainSupervisor.js.map +1 -1
  242. package/dist/server/EnvFunctionality.d.ts +3 -5
  243. package/dist/server/EnvFunctionality.d.ts.map +1 -1
  244. package/dist/server/EnvFunctionality.js +5 -7
  245. package/dist/server/EnvFunctionality.js.map +1 -1
  246. package/dist/server/Functionality.d.ts +3 -2
  247. package/dist/server/Functionality.d.ts.map +1 -1
  248. package/dist/server/Functionality.js +43 -14
  249. package/dist/server/Functionality.js.map +1 -1
  250. package/dist/server/FunctionalityManager.d.ts +3 -4
  251. package/dist/server/FunctionalityManager.d.ts.map +1 -1
  252. package/dist/server/FunctionalityManager.js +17 -9
  253. package/dist/server/FunctionalityManager.js.map +1 -1
  254. package/dist/server/HttpListener.d.ts +2 -0
  255. package/dist/server/HttpListener.d.ts.map +1 -1
  256. package/dist/server/HttpListener.js +23 -9
  257. package/dist/server/HttpListener.js.map +1 -1
  258. package/dist/server/MembersFunctionality.d.ts +2 -5
  259. package/dist/server/MembersFunctionality.d.ts.map +1 -1
  260. package/dist/server/MembersFunctionality.js +5 -3
  261. package/dist/server/MembersFunctionality.js.map +1 -1
  262. package/dist/server/ModuleHost.d.ts +33 -0
  263. package/dist/server/ModuleHost.d.ts.map +1 -0
  264. package/dist/server/ModuleHost.js +2 -0
  265. package/dist/server/ModuleHost.js.map +1 -0
  266. package/dist/server/PlurnkSkill.d.ts.map +1 -1
  267. package/dist/server/PlurnkSkill.js +54 -6
  268. package/dist/server/PlurnkSkill.js.map +1 -1
  269. package/dist/server/ServiceModules.d.ts +0 -2
  270. package/dist/server/ServiceModules.d.ts.map +1 -1
  271. package/dist/server/ServiceModules.js +5 -9
  272. package/dist/server/ServiceModules.js.map +1 -1
  273. package/dist/server/WorkspacePlugins.d.ts +2 -11
  274. package/dist/server/WorkspacePlugins.d.ts.map +1 -1
  275. package/dist/server/WorkspacePlugins.js.map +1 -1
  276. package/dist/server/WorkspaceResidency.d.ts +2 -1
  277. package/dist/server/WorkspaceResidency.d.ts.map +1 -1
  278. package/dist/server/WorkspaceStorage.d.ts.map +1 -1
  279. package/dist/server/WorkspaceStorage.js +7 -4
  280. package/dist/server/WorkspaceStorage.js.map +1 -1
  281. package/dist/server/client-input.d.ts +1 -2
  282. package/dist/server/client-input.d.ts.map +1 -1
  283. package/dist/server/client-input.js +2 -13
  284. package/dist/server/client-input.js.map +1 -1
  285. package/dist/server/dispatch-as-plurnk.js +1 -1
  286. package/dist/server/dispatch-as-plurnk.js.map +1 -1
  287. package/dist/server/drain.sql +6 -6
  288. package/dist/server/envelope.d.ts +2 -1
  289. package/dist/server/envelope.d.ts.map +1 -1
  290. package/dist/server/envelope.js +7 -17
  291. package/dist/server/envelope.js.map +1 -1
  292. package/dist/server/envelope.sql +5 -5
  293. package/dist/server/module-discovery.d.ts +11 -3
  294. package/dist/server/module-discovery.d.ts.map +1 -1
  295. package/dist/server/module-discovery.js +32 -18
  296. package/dist/server/module-discovery.js.map +1 -1
  297. package/dist/server/seam-proposal-list.sql +6 -4
  298. package/dist/service.d.ts.map +1 -1
  299. package/dist/service.js +58 -65
  300. package/dist/service.js.map +1 -1
  301. package/migrations/003_loops.sql +2 -2
  302. package/migrations/006_log.sql +1 -1
  303. package/migrations/016_selection_statements.sql +44 -0
  304. package/migrations/017_skills_module.sql +6 -0
  305. package/migrations/018_worker_owners.sql +102 -0
  306. package/migrations/019_interactive_owners.sql +6 -0
  307. package/migrations/020_log_projection.sql +143 -0
  308. package/package.json +71 -76
  309. package/dist/core/EmissionHead.d.ts +0 -8
  310. package/dist/core/EmissionHead.d.ts.map +0 -1
  311. package/dist/core/EmissionHead.js +0 -29
  312. package/dist/core/EmissionHead.js.map +0 -1
  313. package/dist/core/LegacyHome.d.ts +0 -10
  314. package/dist/core/LegacyHome.d.ts.map +0 -1
  315. package/dist/core/LegacyHome.js +0 -170
  316. package/dist/core/LegacyHome.js.map +0 -1
  317. package/dist/core/LoopPolicies.d.ts +0 -7
  318. package/dist/core/LoopPolicies.d.ts.map +0 -1
  319. package/dist/core/LoopPolicies.js +0 -42
  320. package/dist/core/LoopPolicies.js.map +0 -1
  321. package/dist/core/LoopPolicyReader.d.ts +0 -7
  322. package/dist/core/LoopPolicyReader.d.ts.map +0 -1
  323. package/dist/core/LoopPolicyReader.js +0 -33
  324. package/dist/core/LoopPolicyReader.js.map +0 -1
  325. package/dist/core/LoopPolicyReader.sql +0 -6
  326. package/dist/core/operation-target-groups.d.ts +0 -3
  327. package/dist/core/operation-target-groups.d.ts.map +0 -1
  328. package/dist/core/operation-target-groups.js +0 -77
  329. package/dist/core/operation-target-groups.js.map +0 -1
  330. package/dist/digest/Digest.d.ts +0 -10
  331. package/dist/digest/Digest.d.ts.map +0 -1
  332. package/dist/digest/Digest.js +0 -252
  333. package/dist/digest/Digest.js.map +0 -1
  334. package/dist/digest/DigestEvidence.d.ts +0 -11
  335. package/dist/digest/DigestEvidence.d.ts.map +0 -1
  336. package/dist/digest/DigestEvidence.js +0 -66
  337. package/dist/digest/DigestEvidence.js.map +0 -1
  338. package/dist/digest/DigestRender.d.ts +0 -18
  339. package/dist/digest/DigestRender.d.ts.map +0 -1
  340. package/dist/digest/DigestRender.js +0 -1044
  341. package/dist/digest/DigestRender.js.map +0 -1
  342. package/dist/digest/DigestRequiem.d.ts +0 -13
  343. package/dist/digest/DigestRequiem.d.ts.map +0 -1
  344. package/dist/digest/DigestRequiem.js +0 -337
  345. package/dist/digest/DigestRequiem.js.map +0 -1
  346. package/dist/digest/digest-db.d.ts +0 -3
  347. package/dist/digest/digest-db.d.ts.map +0 -1
  348. package/dist/digest/digest-db.js +0 -68
  349. package/dist/digest/digest-db.js.map +0 -1
  350. package/dist/digest/digest-paths.d.ts +0 -6
  351. package/dist/digest/digest-paths.d.ts.map +0 -1
  352. package/dist/digest/digest-paths.js +0 -14
  353. package/dist/digest/digest-paths.js.map +0 -1
  354. package/dist/digest/digest-rows.d.ts +0 -346
  355. package/dist/digest/digest-rows.d.ts.map +0 -1
  356. package/dist/digest/digest-rows.js +0 -2
  357. package/dist/digest/digest-rows.js.map +0 -1
  358. package/dist/index.d.ts +0 -15
  359. package/dist/index.d.ts.map +0 -1
  360. package/dist/index.js +0 -23
  361. package/dist/index.js.map +0 -1
  362. package/dist/schemes/Skill.d.ts.map +0 -1
  363. package/dist/schemes/Skill.js.map +0 -1
  364. package/dist/server/DaemonModule.d.ts +0 -101
  365. package/dist/server/DaemonModule.d.ts.map +0 -1
  366. package/dist/server/DaemonModule.js +0 -2
  367. package/dist/server/DaemonModule.js.map +0 -1
  368. package/dist/server/SkillSource.d.ts +0 -37
  369. package/dist/server/SkillSource.d.ts.map +0 -1
  370. package/dist/server/SkillSource.js +0 -267
  371. package/dist/server/SkillSource.js.map +0 -1
  372. package/dist/server/SkillsFunctionality.d.ts +0 -45
  373. package/dist/server/SkillsFunctionality.d.ts.map +0 -1
  374. package/dist/server/SkillsFunctionality.js +0 -405
  375. package/dist/server/SkillsFunctionality.js.map +0 -1
  376. package/dist/server/skills-problems.d.ts +0 -8
  377. package/dist/server/skills-problems.d.ts.map +0 -1
  378. package/dist/server/skills-problems.js +0 -18
  379. package/dist/server/skills-problems.js.map +0 -1
  380. package/dist/share/Share.d.ts +0 -15
  381. package/dist/share/Share.d.ts.map +0 -1
  382. package/dist/share/Share.js +0 -106
  383. package/dist/share/Share.js.map +0 -1
  384. package/dist/share/share.sql +0 -6
  385. package/docs/skills.md +0 -73
package/SPEC.md CHANGED
@@ -18,7 +18,7 @@ Canonical contracts plurnk-service exposes, architecture it implements, promises
18
18
  - [Proposals and client interactions](#proposal-proposals-and-client-interactions)
19
19
  - [Stream Model](#stream-stream-model)
20
20
  - [Storage Model](#storage-model)
21
- - [Plugin composition](#core-plugin-composition-plugin-composition)
21
+ - [Extension composition](#core-extension-composition-extension-composition)
22
22
  - [Bundled Set](#bundled-set-bundled-set)
23
23
  - [Grammar Dependency](#grammar-dependency)
24
24
  - [Operator Configuration](#operator-config-operator-configuration)
@@ -55,7 +55,7 @@ flowchart LR
55
55
  | **workspace** | Core | Durable user-named shared world. Persists across workers and process restarts. Identity: `workspaces.id` + unique `workspaces.name`. |
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
- | **turn** | Core | One durable, producer-neutral batch of ordered operations. A turn may be authored by a model, client, plugin, 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)`. |
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)`. |
59
59
  | **model call** | Core/provider | One logical `provider.generate` invocation. Emission attempts and BARE inferences share this durable accounting owner; provider retries remain cardinal physical requests beneath it. Identity: `(turn_id, sequence)`. |
60
60
  | **op** | Producer/core | One DSL operation a producer submits, parsed into a `PlurnkStatement`. Admission follows {§turn-ops-admission-path}. |
61
61
  | **statement** | Model/core | A parsed op: the `PlurnkStatement` AST from `@plurnk/plurnk-contracts`. |
@@ -63,8 +63,6 @@ flowchart LR
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
65
  | **AG-UI thread** | AG-UI protocol | Conversation identity. Within an explicitly selected workspace, `threadId` resolves to one conversation worker. |
66
- | **`--run`** | Client compatibility | A compatibility-sensitive client spelling, not an internal entity. |
67
- | **session** | Retired/unqualified | Not a PLURNK lifecycle noun. Use the actual core noun; a third-party standard may use only its explicitly qualified protocol term. <!-- lexicon-allow: this row defines the retired noun --> |
68
66
 
69
67
  ### Storage terms
70
68
 
@@ -72,7 +70,7 @@ flowchart LR
72
70
  |---|---|
73
71
  | **entry** | The unit of canonical state. Identity: `(workspace_id, scheme, authority, pathname)` ({§entry-identity-no-null}). Holds one or more `channels` of content plus scheme-private `attributes`. |
74
72
  | **channel** | A named content buffer on an entry. Examples: `body`, `stdout`, `stderr`, `headers`, `symbols`. Each channel has `content`, `mimetype`, curation `weight`, and lifecycle `state`. |
75
- | **scheme** | An addressed capability family + handler. Built-ins include `worker`, `log`, `ops`, `reasoning`, and bare/file paths; discovered schemes and executor-runtime tags extend that set. Internal `exec` routes executions but is not an addressable model namespace. Consumption surface {§scheme-surface}; author contract: [plurnk-schemes](../plurnk-schemes/SPEC.md). |
73
+ | **scheme** | An addressable namespace and its handler. Built-ins include `worker`, `log`, `ops`, `reasoning`, and bare/file paths; discovered schemes and executor-runtime tags extend that set. Internal `exec` routes executions but is not an addressable model namespace. Consumption surface {§scheme-surface}; author contract: [plurnk-schemes](../plurnk-schemes/SPEC.md). |
76
74
  | **mimetype** | A channel's content type. Drives the handler that produces the structural projections (`symbols`, `deepJson`, `deepXml`). Consumption surface {§mimetype}; author contract: [plurnk-mimetypes](../plurnk-mimetypes/SPEC.md). |
77
75
  | **provider** | An LLM transport implementing the `@plurnk/plurnk-providers` `Provider` interface. Core supplies an assembled request and generation context; the provider owns endpoint adaptation and normalized response evidence. Consumption surface {§provider-surface}; author contract: [plurnk-providers](../plurnk-providers/SPEC.md). |
78
76
 
@@ -91,9 +89,9 @@ Independent axes on entries and channels. Confusion across them is a recurring s
91
89
 
92
90
  | Term | Meaning |
93
91
  |---|---|
94
- | **writer** | The identity authoring a write. One of `model \| client \| _plurnk \| plugin`. Carried on `ctx.writer` for schemes; engine enforces `manifest.writableBy`. |
92
+ | **writer** | The identity authoring a write. One of `model \| client \| _plurnk`. Carried on `ctx.writer` for schemes; engine enforces `manifest.writableBy`. |
95
93
  | **origin** | Synonym for writer in log_entries (`log_entries.origin`). Synonym for writer. |
96
- | **writable_by** | The set of writers a scheme accepts. Subset of `{model, client, _plurnk, plugin}`. Engine rejects writes outside the set with 403; the rejection is logged as the action-entry ({§subscriptions} action-entry-as-outcome). |
94
+ | **writable_by** | The set of writers a scheme accepts. Subset of `{model, client, _plurnk}`; empty means no operation writes the scheme. Engine rejects writes outside the set with 403; the rejection is logged as the action-entry ({§subscriptions} action-entry-as-outcome). |
97
95
 
98
96
  ### Execution terms
99
97
 
@@ -105,8 +103,8 @@ Independent axes on entries and channels. Confusion across them is a recurring s
105
103
  | **BARE inference** | One isolated child-provider model call whose response becomes an ordinary BARE log result. It has no worker, packet, tools, output grammar, or persistent child state ({§bare-inference}). |
106
104
  | **cycle** | Repeated operational inputs and observed results under {§engine-cycle-evidence}. |
107
105
  | **capability policy** | A purely subtractive `only`/`deny` selector layer over routed operation demands. Service and workspace layers compose without granting authority. |
108
- | **loop policy** | One immutable `review`, `accept`, or `reject` proposal disposition. |
109
- | **proposal** | A deferred side-effecting action. State machine: `proposed → resolved` (accept), `→ failed` (reject), or `→ cancelled` (cancel). Its core-owned disposition says whether the client or loop owns resolution ({§proposal-disposition}). |
106
+ | **approval owner** | One durable, workspace-scoped recipient for a worker's approval requests ({§worker-ownership}). |
107
+ | **proposal** | A deferred side-effecting action. State machine: `proposed → resolved` (accept), `→ failed` (reject), or `→ cancelled` (cancel). Its core-owned disposition selects owner review or automatic settlement ({§proposal-disposition}). |
110
108
  | **resolution** | A client decision delivered through a standard resume entry. Proposal resolutions accept, reject, or cancel ({§methods-proposal-resolve}); client-interaction resolutions return a payload or cancel ({§methods-client-interaction-resolve}, {§agui-proposal-resolve}). |
111
109
 
112
110
  ### §packet-terms Packet terms
@@ -133,7 +131,7 @@ package map. The default installed composition is specified in {§bundled-set}.
133
131
 
134
132
  OpenTelemetry may observe PLURNK; it never becomes product state, failure transport, scheduler input, model teaching, or client protocol. Domain and client activity remain on AG-UI. Reusable packages depend on the OTel API only; the daemon constructs only the explicitly configured trace and metric providers. An unconfigured or standards-valid disabled process loads no SDK or exporter implementation and keeps the API's no-op behavior with bounded overhead. OTel Logs have no provider or initialization path.
135
133
 
136
- Configuration uses the standard `OTEL_*` environment: `OTEL_TRACES_EXPORTER` / `OTEL_METRICS_EXPORTER` select `otlp` or `console` per signal (a missing or `none` value keeps that signal off; no SDK default selects an exporter), `OTEL_SERVICE_NAME` names the service (default `plurnk-service`), case-insensitive `true` in `OTEL_SDK_DISABLED` turns the boundary off, and OTLP exporters honor `OTEL_EXPORTER_OTLP_*`. An unknown exporter name makes observability unavailable with a configuration diagnostic ({§configuration-repair-path}); offline checking rejects the same input before loading an SDK. OTel Logs and direct draft semantic-convention use are excluded. HTTP spans carry only an AG-UI-owned bounded route class, never an input pathname or query. Spans otherwise carry high-cardinality identifiers; metric labels stay low-cardinality. Prompts, reasoning, file bodies, arbitrary URLs, secrets, and plugin payloads are never recorded as attributes or metric values by default. Exporter failure cannot change product results or client lifecycle. Daemon, telemetry, and database teardown are independent reverse-ownership phases; every phase runs and aggregate failure preserves every cause.
134
+ Configuration uses the standard `OTEL_*` environment: `OTEL_TRACES_EXPORTER` / `OTEL_METRICS_EXPORTER` select `otlp` or `console` per signal (a missing or `none` value keeps that signal off; no SDK default selects an exporter), `OTEL_SERVICE_NAME` names the service (default `plurnk-service`), case-insensitive `true` in `OTEL_SDK_DISABLED` turns the boundary off, and OTLP exporters honor `OTEL_EXPORTER_OTLP_*`. An unknown exporter name makes observability unavailable with a configuration diagnostic ({§configuration-repair-path}); offline checking rejects the same input before loading an SDK. OTel Logs and direct draft semantic-convention use are excluded. HTTP spans carry only an AG-UI-owned bounded route class, never an input pathname or query. Spans otherwise carry high-cardinality identifiers; metric labels stay low-cardinality. Prompts, reasoning, file bodies, arbitrary URLs, secrets, and extension payloads are never recorded as attributes or metric values by default. Exporter failure cannot change product results or client lifecycle. Daemon, telemetry, and database teardown are independent reverse-ownership phases; every phase runs and aggregate failure preserves every cause.
137
135
 
138
136
  §observability-genai-conventions **GenAI convention projection.** Provider
139
137
  request spans follow the [GenAI registry at `c88d504`](https://github.com/open-telemetry/semantic-conventions-genai/tree/c88d504ab3d9879f8e50d3cc87e69775e11db234),
@@ -157,39 +155,34 @@ draft convention is projected.
157
155
  The process and package map is ARCHITECTURE.md's; this package's AGENTS.md maps core's internal
158
156
  owners. Capability-specific behavior remains with the owning plug point.
159
157
 
160
- §service-worker-composition The service launcher and the live/demo workspace
161
- helper share one registration of default worker-facing modules: MCP, outbound
162
- A2A, and Schedule. Their management families and readable reference documents are
158
+ §service-worker-composition The service launcher composes no module: it and the live/demo
159
+ workspace helper both discover AG-UI, A2A, MCP, Schedule and command hooks ({§module-discovery}). The
160
+ management families and readable reference documents are
163
161
  present even with no enabled definitions. Workspace capability policy controls every actor's
164
162
  surface; registering a family does not enable its definitions. The real-model
165
163
  profile ({§operator-config-real-model-profile}) leaves ambient MCP attachments and
166
164
  service schedules disabled by default; specimens may add their own through the
167
- ordinary management surface. Client and
168
- inbound-A2A listeners and host hooks remain launcher-owned.
165
+ ordinary management surface. The one listener is the launcher's ({§http-host}); the helper binds
166
+ none, so its daemon leaves out AG-UI ({§module-http-mounts}).
169
167
 
170
168
  ### §service-package-exports Package export surface
171
169
 
172
- | Export path | Current contract |
173
- |---------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
174
- | `@plurnk/plurnk-service` | Frozen 1.x compatibility barrel of the exports below. It gains no new APIs and is not the client boundary. Removal is SemVer-major. |
175
- | `@plurnk/plurnk-service/digest` | Supported programmatic forensic surface owned by {§digest-programmatic-surface}. |
176
- | `@plurnk/plurnk-service/package.json` | Supported package metadata surface. |
170
+ | Export path | Current contract |
171
+ |---------------------------------------|------------------|
172
+ | `@plurnk/plurnk-service/launch` | The daemon launcher ({§daemon-launch}). |
173
+ | `@plurnk/plurnk-service/evidence` | Canonical evidence reader consumed by the separate report package ({§digest-evidence-reader}). |
174
+ | `@plurnk/plurnk-service/package.json` | Supported package metadata surface. |
177
175
 
178
- | Root exports | Names |
179
- |--------------|-------|
180
- | Runtime | `Daemon`, `Engine`, `EnvFlags`, `Exec`, `File`, `Log`, `Mimetypes`, `Mock`, `Paths`, `SchemeRegistry`, `Skill` |
181
- | Types | `ChatMessage`, `EditResult`, `FlagDescriptor`, `MockAssistant`, `MockResponse`, `OpenFoldResult`, `ReadResult` |
182
-
183
- New clients use AG-UI. A new library contract belongs in its owning package or
184
- an explicitly specified subpath, not in the frozen root barrel.
176
+ These are the package's only export paths. Clients use AG-UI; a new library contract belongs in its
177
+ owning package or an explicitly specified subpath.
185
178
 
186
179
  ### §startup-admission Startup admission
187
180
 
188
181
  ```mermaid
189
182
  flowchart LR
190
- LISTENER["Bind client listener<br/>unready: HTTP 503"] --> DB["Acquire daemon lock<br/>and admit SQLite schema"]
191
- DB --> DAEMON["Construct and start<br/>daemon composition"]
192
- DAEMON --> CLIENT["Activate client transport"]
183
+ LISTENER["Bind the daemon's listener<br/>unadmitted: HTTP 503"] --> DB["Acquire daemon lock<br/>and admit SQLite schema"]
184
+ DB --> DAEMON["Start the daemon:<br/>discover and start modules"]
185
+ DAEMON --> CLIENT["Admit the listener"]
193
186
  CLIENT --> SELECT["Worker selects or first uses a model"]
194
187
  SELECT --> PROVIDER["Construct and verify<br/>selected provider"]
195
188
  LISTENER -. failure .-> FAIL["Fail startup<br/>durable state untouched"]
@@ -202,9 +195,9 @@ flowchart LR
202
195
  ({§http-host}) before creating, opening, replacing, rotating, migrating, or
203
196
  otherwise mutating anything in the durable data directory. A process that loses
204
197
  the listener race fails with the originating address error and byte-identical
205
- durable storage. Core owns the socket continuously; it answers 503 until the
206
- client-interface module mounts the root at daemon activation, so early
207
- ownership introduces neither traffic nor a close/rebind race.
198
+ durable storage. Core owns the socket continuously; it answers 503 until daemon
199
+ activation admits it after every module has started ({§module-http-mounts}), so
200
+ early ownership introduces neither traffic nor a close/rebind race.
208
201
 
209
202
  §http-host **The daemon opens exactly one transport.** Core binds the HTTP
210
203
  listener on `PLURNK_HOST:PLURNK_PORT` and offers it to every exterior adapter as
@@ -212,12 +205,14 @@ listener on `PLURNK_HOST:PLURNK_PORT` and offers it to every exterior adapter as
212
205
  ({§application-port}). A prefix is an absolute pathname. Each request goes to the
213
206
  longest mounted prefix; a prefix claims itself and the subtree beneath it, never
214
207
  a longer sibling name; `/` is the root and receives whatever nothing more
215
- specific claimed. Until a root is mounted the listener answers `503
208
+ specific claimed when a module mounts it. An admitted listener returns an HTTP
209
+ `404 route-not-found` Problem for any path without a mounted handler; a root mount
210
+ is not required. Until daemon activation admits it the listener answers `503
216
211
  service-starting` to every request: the service has not admitted its client
217
- interface. Adapters mount at `start()`, after durable lifecycle recovery, and
218
- none opens a socket of its own under the daemon — a module hosted *without* a
219
- daemon may bind a private one, which is outside this contract. The standards
220
- address by URL, never by port (#641): AG-UI mounts `/` and `/agui`, A2A the
212
+ interface. Adapters claim their prefixes before setup and mount them at
213
+ `start()`, after durable lifecycle recovery ({§module-http-mounts}), and
214
+ none opens a socket of its own. The standards
215
+ address by URL, never by port (#641): AG-UI mounts only `/agui`, A2A the
221
216
  well-known card and its endpoint path, on the same address.
222
217
 
223
218
  §startup-admission-order After listener ownership, database admission completes
@@ -230,7 +225,7 @@ preserving the originating failure: daemon, observability, database, listener.
230
225
 
231
226
  §startup-readiness-line **Readiness is one stdout line.** After the client interface is mounted
232
227
  the service prints exactly one line, `plurnk-service agui=<url> db=<json string> route=<json string>`:
233
- the URL brackets an IPv6 host, and the database path and the route (the active model route or
228
+ the URL includes `/agui` and brackets an IPv6 host, and the database path and the route (the active model route or
234
229
  `no model`, or `invalid model configuration`) are JSON strings, so a path or route containing spaces is exact and a consumer parses
235
230
  the URL as a URL and the strings as JSON; nothing else the service prints on stdout before it has
236
231
  that prefix. Before the line the listener answers `503 service-starting`; after it,
@@ -256,6 +251,49 @@ launchers consume the same contract by reading the line themselves.
256
251
 
257
252
  ## §actor-boundary Workers and workspace boundaries
258
253
 
254
+ ### §worker-ownership Approval ownership
255
+
256
+ Every worker has one workspace-scoped approval owner. Ownership is independent of
257
+ origin, parentage, messages and connection presence. It controls approval routing,
258
+ not access to workspace resources ({§actor-boundary-no-mutex}).
259
+
260
+ | Event | Ownership |
261
+ | --- | --- |
262
+ | Root creation | Explicit owner, otherwise the runtime `_plurnk`. |
263
+ | WORK, FORK, or a fresh child | Inherit the parent's owner. |
264
+ | 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. |
266
+ | Observation or disconnect | No change. |
267
+
268
+ An owner declares its capability set: the client tool names it implements, and whether it
269
+ is interactive, that is, whether a person attends it. The declaration remains durable while it
270
+ is disconnected. A proposal requiring review waits for that owner; unsupported review fails at
271
+ the request boundary. Approval needs no person: a client that accepts automatically still
272
+ implements `request_approval`. Clarification and a park only a person can end need an
273
+ interactive owner. `_plurnk` implements no client tools and is never interactive. Server
274
+ approval policy may settle a proposal automatically; it cannot fabricate an interaction answer.
275
+ Kernel maintenance remains kernel work ({§actor-boundary-self-hosting}).
276
+
277
+ §worker-owner-creation Ownership, inheritance, and runtime-owner creation are
278
+ database invariants. A newly created worker cannot be left without an owner;
279
+ an unknown explicit owner is an error. Existing workers migrate to runtime ownership.
280
+
281
+ §worker-owner-resolution A proposal names its worker's owner and one disposition:
282
+ `review`, `accept`, or `reject`. Review routes to that owner, not to the sender,
283
+ the current observer, or whichever ancestor has a live Run. A decision is accepted
284
+ only for that owner and the still-pending gate. Disconnect does not substitute a
285
+ different owner. Capability admission remains independent and precedes approval.
286
+
287
+ §client-interaction-routing Clarification goes to the worker's owner, like approval, but
288
+ only a person can answer it. The owner receives it while it is interactive and declares
289
+ the requested tool; otherwise the request fails at once as unsupported and never parks.
290
+ The pending interaction records that recipient and accepts a response only from it,
291
+ through the ordinary response-schema validation and settlement path. A protocol caller,
292
+ such as an A2A client, converses by message and is never a recipient
293
+ ({§a2a-worker-ownership}).
294
+
295
+ ### Worker boundaries
296
+
259
297
  ```mermaid
260
298
  flowchart LR
261
299
  child["Child worker"] -->|"durable activity<br/>environment door"| parent["Direct parent log"]
@@ -277,11 +315,12 @@ filter a row.
277
315
 
278
316
  §actor-boundary-attached-functionality **Attachment selects a conversation,
279
317
  not an environment owner.** Client management commands mutate workspace
280
- Functionality ({§module-workspace-capabilities}); executable families, runtime
318
+ Functionality ({§module-workspace-capabilities}); runtimes, runtime
281
319
  schemes, and tools resolve by `workspaceId`. Operations journal in the
282
320
  submitting actor's `workerId` ({§connection-lifecycle}), which must belong to
283
321
  the workspace. Runtime and policy resolution require no second worker identity.
284
- Attachment remains many-to-one, non-owning, and unrelated to parentage.
322
+ Attachment remains many-to-one and unrelated to parentage. Explicit control
323
+ attachment may claim approval ownership under {§worker-ownership}.
285
324
 
286
325
  §actor-boundary-two-doors **Cross-worker arrival is limited to two doors.**
287
326
  An explicit READ is not an arrival: the reading worker deliberately addresses a
@@ -363,7 +402,6 @@ narrow or omit the reference catalogs under {§capability-admission}.
363
402
  | Project filesystem | `*` | File cap below; `project root member files` |
364
403
  | Workspace entries | `worker:///*` | Markerless; `workspace knowledgebase entries` |
365
404
  | Named scratch entries | `worker://<worker>/*` | Markerless; `worker knowledgebase entries` |
366
- | The worker's own reasoning | `reasoning://<worker>/**` | Markerless; `this worker's reasoning, by loop and turn` |
367
405
 
368
406
  Only the three namespace surveys carry asides; the other targets name
369
407
  their surface. The word `skills` names Agent Skills and nothing else.
@@ -371,7 +409,7 @@ Catalogs select documents independently of their authored bodies; ordinary READ
371
409
  supplies examples and complete instructions on demand. A shallow
372
410
  result renders direct entries normally and every deeper first-segment directory
373
411
  as an actionable `dir/**` summary with its recursive `items` and `tokens`;
374
- tool-family rows also carry the concise `{§scheme-catalog-aside}` that drives
412
+ runtime rows also carry the concise `{§scheme-catalog-aside}` that drives
375
413
  on-demand capability discovery. Ordinary surveys use FIND without a scope
376
414
  ({§markerless-first-page}), whose range metadata reports the returned set against the
377
415
  complete result total; only the small capability-reference surfaces
@@ -460,7 +498,7 @@ existing workspace worker in its own right.
460
498
 
461
499
  §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.
462
500
 
463
- §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 plugin-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}).
501
+ §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}).
464
502
 
465
503
  §worker-provider-identity **A worker owns a durable provider identity distinct
466
504
  from its database id.** Creation mints a globally unique, opaque 128-bit value;
@@ -496,7 +534,7 @@ continues to decompose other authorities without treating them as mintable.
496
534
 
497
535
  §worker-read-scope **Scratch is workspace-readable.** Any actor reads and searches any named or shared scratch address. Parentage and writer identity do not change resolution. Scratch namespaces do not require a namesake Worker. A pathless actor address requires a named Worker; an unknown actor returns 404.
498
536
 
499
- §worker-write-scoping **Scratch is workspace-writable.** All workspace actors may EDIT, COPY, MOVE, or KILL entries in any named or shared scratch namespace, including generated documents. There is no creator-only, self-only, ancestor-only, or runtime-only grant. Workspace admission remains uniform. Intrinsically immutable evidence in other schemes retains its own contract ({§scheme-entry-matrix}); operation provenance and delegation lifecycle do not grant or restrict scratch access.
537
+ §worker-write-scoping **Scratch is workspace-writable.** All workspace actors may EDIT, COPY, MOVE, or KILL entries in any named or shared scratch namespace, including generated documents. Workspace admission remains uniform. Intrinsically immutable evidence in other schemes retains its own contract ({§scheme-entry-matrix}); operation provenance and delegation lifecycle do not grant or restrict scratch access.
500
538
 
501
539
  §worker-generated-subtree **Generated documents share `worker:///_plurnk/`.** Project instructions (`AGENTS.md` and subtree-scoped `instructions/**`), scheme/runtime references (`plurnk/**`), tool details (`tools/**`), and family catalogs are workspace resources. Agent Skills retain their own trees at `skill://<name>/` ({§skills-resources}).
502
540
 
@@ -551,7 +589,6 @@ Every admitted authority is a literal `workers.name`; self-addressing uses the c
551
589
  own work is a fresh loop, so an inherited mid-flight loop never makes the
552
590
  branch look forever-live to the {§send-premature-terminate} gate.
553
591
  - §worker-scheme-fork-scratch **Forked scratch.** Named scratch and evidence are copied under the new name through {§machine-processes-entry-inheritance}. Parent and branch can edit either scratch namespace; their copies diverge independently.
554
- - §worker-delegation-inherits-policy **Fresh delegated loops inherit the sender's policy.** WORK, FORK, and SEND to an idle Worker carry the sender's complete loop policy, disposition and attendance alike. SEND into an active or parked loop leaves its immutable policy untouched. All workers share live workspace capability policy; delegation creates no capability snapshot or bound.
555
592
  - §worker-lifecycle-wake-requeue-not-terminal **A wake re-queue is not a terminal.** A conclusion-wake resumes a 202-blocked loop by re-queueing it (202 → 100); when that lands while the loop's own live drain is between turns, the drain **re-claims and continues** (atomic 100 → 102; the injected prompt is already the next turn). The internal re-queue is never reported as an outward terminal.
556
593
 
557
594
  - §worker-scheme-collect **Collect** — each concluded child loop reaches its direct
@@ -595,7 +632,7 @@ Every admitted authority is a literal `workers.name`; self-addressing uses the c
595
632
  starts there too, then resets atomically when any published channel's content
596
633
  changes, including replacement or clearing. Identical writes, empty appends,
597
634
  READs, publication acknowledgements, metadata, and channel-state changes do
598
- not reset it. The clock is durable; an upgraded stream lacking that evidence
635
+ not reset it. The clock is durable; a stream lacking that evidence
599
636
  reports `output timing unknown` until its next content change, never a guessed
600
637
  timestamp. These are observations, not new timeouts or wake conditions
601
638
  ({§exec-stream}). It is orienting state, never advice: the model sees its live
@@ -856,7 +893,6 @@ cancels work or fabricates a result.
856
893
  With live work, each WAIT receipt exposes `waitSeconds`, the maximum accepted
857
894
  for that operation. It is not elapsed time or a promised sleep: an ordinary wake
858
895
  or a shorter sibling WAIT can resume the loop sooner. An idle WAIT has no bound.
859
- Historical unbounded receipts do not acquire one during projection.
860
896
 
861
897
  | Boundary | Outcome |
862
898
  |---|---|
@@ -914,12 +950,6 @@ move it. The digest reports, per loop, the claim time and how long after it the
914
950
  first model turn started, so a stall between claim and inference (a heartbeat has
915
951
  waited 7.5 h, #703) is a number rather than a gap.
916
952
 
917
- §digest-storage **The digest states the file's health.** Beside the database path it
918
- reports the file size, the free pages it holds, its `auto_vacuum` mode, and the six
919
- largest tables and indexes by allocated bytes (`dbstat`), so growth is a number in
920
- every digest (#764). The digest reads loops as stored, so it tolerates databases
921
- missing later lifecycle columns.
922
-
923
953
  §loop-execution-allowance **One task has one execution allowance.** The first
924
954
  execution snapshots `PLURNK_SERVICE_LOOP_TIMEOUT` on the loop. Active segments
925
955
  consume that allowance cumulatively, measured with a monotonic clock; waits and
@@ -1079,7 +1109,7 @@ boundary.
1079
1109
  | Independent arrival after cancellation | Admit a new loop above the cutoff using the ordinary worker policy. |
1080
1110
  | Slow stream teardown after new admission | Reap only subscription identities captured by cancellation; late old-scope spawns follow {§worker-lifecycle-exec-epoch-bound}. |
1081
1111
 
1082
- - §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.** The stream first persists its terminal state. A worker **blocked on a 202 wait** for that stream ({§wait-obligation-matrix}) then **awakens that loop in place** — the blocked loop *is* the continuation, so there is no fresh loop and no summary-as-prompt fiction. An already-active worker needs no injected prompt or second wake because its next packet reads the durable terminal state. A concluded worker receives no synthetic loop from ambient stream closure. The result remains available in the stream's own state under every case.
1112
+ - §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.** The stream first persists its terminal state. A worker **blocked on a 202 wait** for that stream ({§wait-obligation-matrix}) then **awakens that loop in place** — the blocked loop *is* the continuation. An already-active worker's next packet reads the durable terminal state. Ambient stream closure starts no loop for a concluded worker. The result remains available in the stream's own state under every case.
1083
1113
  - §worker-lifecycle-child-wake **Each child task completion notifies its parent.** Terminal-task publication, including failure and cancellation of a parked task, notifies the direct parent without injecting a prompt. Other unfinished tasks or streams in that child remain independent obligations; they cannot suppress notification. The parent's eligible waits requeue in place under {§loop-wake-identity} and the bounded {§worker-optimistic-settlement} opportunity. Durable revisioning covers completion-before-park and restart; drain teardown and whole-worker quiescence are not completion identities.
1084
1114
  - §worker-optimistic-settlement **Asynchronous settlement receives one bounded worker-local opportunity before model dispatch.** An initiating turn lets only the streams it started settle before program completion; separately, a stream conclusion, direct-child conclusion or addressed reply persists and publishes immediately but holds eligible parked loops' `202→100` requeues while another stream or direct child remains live. Both use `PLURNK_SERVICE_OPTIMISTIC_WAIT_MS`, shipped at five seconds; zero disables the opportunity. The wake hold ends as soon as no sibling obligation remains, never extends its original deadline, and coalesces arrivals within that window into at most one requeue per eligible loop. With no sibling obligation the wake is immediate; at the deadline, surviving work follows the ordinary monitored lifecycle. An arrival after provider dispatch begins retains its next wake, while poll, new-request and operator wakes never open this hold. Only packet/provider dispatch waits: durable state, client events, cancellation and the replying program do not. One redaction-safe span records elapsed time, quiescence versus deadline, and arrival count without entering the packet.
1085
1115
  - §worker-lifecycle-idle-is-concluded **Idle is not completion.** An empty WAIT continues; an eligible parameterless KILL with observed arrivals and results and no held work concludes under {§wait-obligation-matrix}, with or without an answer body. A concluded worker retains durable history; a later addressed arrival starts a new loop.
@@ -1093,11 +1123,11 @@ boundary.
1093
1123
 
1094
1124
  A turn is the durable container for one producer's ordered operations. Packet
1095
1125
  and provider fields are optional evidence belonging only to model inference;
1096
- their absence never makes a client, plugin, or `_plurnk` turn exceptional.
1126
+ their absence never makes a client or `_plurnk` turn exceptional.
1097
1127
 
1098
1128
  | Field | Contract |
1099
1129
  |---|---|
1100
- | `producer` | Required actor class: `model`, `client`, `plugin`, or `_plurnk`. |
1130
+ | `producer` | Required actor class: `model`, `client`, or `_plurnk`. |
1101
1131
  | `kind` | Required purpose: `inference`, `initialization`, `operation`, or `maintenance`. Model iff inference; initialization and maintenance require `_plurnk`. Producer and kind are immutable. A maintenance turn's successful rows are packet-suppressed — a receipt answers an asker, and maintenance has none ({§actor-boundary-doc-injection}). |
1102
1132
  | `status`, `completed_at` | A new turn is open at status 102 with `completed_at=NULL`. Completion records the program outcome and timestamp; a completed 102 is distinct from an open 102. A successful administrative program completes at 200 without concluding its host model loop. |
1103
1133
  | Operations | Ordered by `(turn_id, sequence)` on one exact worker/loop/turn chain. Each row's `origin` is the turn producer or `_plurnk` making a system observation; the observation does not impersonate the producer. |
@@ -1106,9 +1136,8 @@ their absence never makes a client, plugin, or `_plurnk` turn exceptional.
1106
1136
 
1107
1137
  One lifecycle owner opens, optionally records inference evidence, and completes
1108
1138
  every turn. Initialization, maintenance, client dispatch, and model
1109
- inference use that same path. `plugin` is the producer identity for
1110
- plugin-authored operation turns; exposing that path must not introduce a
1111
- parallel record or lifecycle. Producer and kind never change. Process-restart
1139
+ inference use that same path; a new producer joins it without a parallel
1140
+ record or lifecycle. Producer and kind never change. Process-restart
1112
1141
  recovery completes any turn whose producer vanished.
1113
1142
 
1114
1143
  §turn-exception-outcome An exceptional inference exit completes every still-open
@@ -1122,7 +1151,7 @@ The turn owner propagates the original exception unchanged.
1122
1151
  | An `AggregateError` combining cancellation with other failures | 500; cancellation does not conceal another failure. |
1123
1152
 
1124
1153
  §turn-ops-admission-path **Source acquisition varies; admitted-turn execution does not.**
1125
- A provider response, deterministic `_plurnk` program, or future client/plugin
1154
+ A provider response, deterministic `_plurnk` program, or future client
1126
1155
  program crosses one admission boundary into the same executor. That executor
1127
1156
  parses once, dispatches the admitted statements in order, records their ordinary
1128
1157
  outcomes, and completes the turn from its lifecycle ruling. Exact source is retained before dispatch.
@@ -1206,9 +1235,9 @@ retains only the current provider state; the next completed exchange notices
1206
1235
  `provider_recovered`. Recovery is bounded by `PLURNK_SERVICE_PROVIDER_RECOVERY`; when it
1207
1236
  is spent the turn completes as `202` and the loop parks exactly like a
1208
1237
  WAIT ({§worker-lifecycle-wake-requeue-not-terminal}), resuming on the
1209
- next prompt or wake with its log intact — **unless the run is unattended
1210
- ({§loop-attendance}), in which case the loop concludes on the provider's exact failure
1211
- instead, because parking stops the execution clock and no wake would ever arrive.** Only a client cancel, the execution allowance
1238
+ next prompt or wake with its log intact — **unless the worker's owner is not interactive
1239
+ ({§worker-ownership}); then the loop concludes on the provider's exact failure because no
1240
+ person attends to wake that park.** Only a client cancel, the execution allowance
1212
1241
  ({§operator-config-loop-timeout}), or a non-recoverable provider Problem (refusal,
1213
1242
  authorization, quota, an invalid response) settles a loop on a provider failure.
1214
1243
 
@@ -1325,7 +1354,7 @@ The parser owns its boundaries; core admits determinate work and exposes its fai
1325
1354
  | Lost boundary before any closed operation | Reject the attempt; outside text is no substitute for a closed operational statement. An admitted reasoning operation is one ({§reasoning-operations}). |
1326
1355
  | Outside text carrying a log-entry heading, other than an emission row's | Reject the attempt ({§fabricated-log-entry}). |
1327
1356
 
1328
- §fabricated-log-entry **Only the harness writes the log.** A line of outside response text that begins with a log-entry heading, `### log:///<loop>/<turn>/<sequence>/` ({§log-wire-format}), is the model continuing the packet's transcript instead of answering it: it writes the receipts it expects and then acts on them. The attempt is rejected under {§invalid-emission-attempts}, so neither that text nor any operation beside it runs or is stored as outside text ({§outside-text}), and its one diagnostic, at the heading's line, reads `` `### log:///2/1/5/READ` is a log entry, and only the harness writes the log. Write the operation, then wait for its receipt. `` Text inside an operation body is not examined, so a SEND or KILL may quote a receipt. A heading whose leaf is `emission` is exempt: the transcript shows it before each of the worker's own emissions ({§emission-row}), and repeating it invents no receipt, so the attempt is admitted and the heading stays outside text, counted by the digest as an echo. In 10,486 recorded emissions, 101 carried such a heading in outside text, every one a fabrication: 85 of 1,675 from deepseek-flash, 81 of them opening with one, and 16 from glm-5.3-flash, deepseek-v4-pro and qwen3.8-flash, which appended an invented `## Log` after their own operations.
1357
+ §fabricated-log-entry **Only the harness writes the log.** A line of outside response text that begins with a log-entry heading, `### log:///<loop>/<turn>/<sequence>/` ({§log-wire-format}), is the model continuing the packet's transcript instead of answering it: it writes the receipts it expects and then acts on them. The attempt is rejected under {§invalid-emission-attempts}, so neither that text nor any operation beside it runs or is stored as outside text ({§outside-text}), and its one diagnostic, at the heading's line, reads `` `### log:///2/1/5/READ` is a log entry, and only the harness writes the log. `` Text inside an operation body is not examined, so a SEND or KILL may quote a receipt. A heading whose leaf is `emission` is exempt: it names the worker's own source ({§emission-row}), and repeating it invents no receipt, so the attempt is admitted and the heading stays outside text, counted by the digest as an echo. In 10,486 recorded emissions, 101 carried such a heading in outside text, every one a fabrication: 85 of 1,675 from deepseek-flash, 81 of them opening with one, and 16 from glm-5.3-flash, deepseek-v4-pro and qwen3.8-flash, which appended an invented `## Log` after their own operations.
1329
1358
 
1330
1359
  Warnings and closer recovery ({§closer-fallback}) do not reject. `finish=length`
1331
1360
  discloses truncation and precludes completion; it is not independently a rejection.
@@ -1333,7 +1362,11 @@ Provider interruption is owned by {§provider-interrupted-attempt}. Accepted sou
1333
1362
  and positions remain exact; execution follows {§op-execution-order}. WAIT remains
1334
1363
  optional, with no omission warning or invented operation ({§turn-shape}).
1335
1364
 
1336
- §safe-uri-target-groups After source and authored-command admission, Core expands one target group on READ or KILL into one ordinary statement per member, each with an independent dispatch outcome and log row, in authored member order at that operation's position under {§op-execution-order}. A group is either the heading's slot group ({§target-group}) — any target kind, each member with the scope, metadata and matcher its own slot carried, the heading's naked pattern every member's, and a distilling body landing once with the first member — or one slot whose raw target splits at top-level comma or whitespace separators into at least two members that each independently parse as an explicit `scheme://` URI, the selection shared; request-metadata blocks are opaque to that split, and otherwise the slot's target remains exactly singular, including local filenames containing spaces or commas. The same expansion meets a client-authored group at the client seam. The stored `turnOps` and authored command count remain unexpanded, a group never reaches dispatch unexpanded, and no other operation admits target groups.
1365
+ The parser compiles target groups ({§target-group}, {§safe-uri-target-groups}) into ordinary
1366
+ statements before model or client admission. Core does not reinterpret target syntax. Each
1367
+ admitted member receives its independent dispatch outcome and log row, in authored order under
1368
+ {§op-execution-order}; one member's runtime failure does not suppress later members. The original
1369
+ emission in `turn_sources` remains exact and unexpanded.
1337
1370
 
1338
1371
  Core retries a rejected emission against the exact same packet beneath the same engine turn, up to `PLURNK_SERVICE_EMISSION_ATTEMPTS`. Rejected bytes never dispatch or reach the engine strike rail. Before each `generate`, Core opens one durable logical `inference_calls` row with its `model_calls` specialization and emission-specific `turn_attempts` admission row. A call that ends without response evidence leaves that admission row unclassified (`accepted IS NULL`) and does not consume the emission-attempt ceiling. Beneath the logical call, every provider observer invocation opens one cardinal `provider_requests` occurrence immediately before physical I/O and settles it as response or error. Adapter retries and capacity failover append requests in issue order; a response-less failure therefore remains an accounted occurrence rather than disappearing. Normalized response evidence is durable before parser classification and does not duplicate the separately owned accounting. The accepted exchange alone extends `turns.packet` with response evidence; every physical request remains in turn and loop accounting, while the context gauge reads the latest settled emission request on the latest turn. Digest exposes rejected response evidence as `<stem>.attemptNNN.rejected.*` and every physical request in its machine-readable ledger.
1339
1372
 
@@ -1371,10 +1404,10 @@ interrupted response evidence, Core stores it unaccepted without duplicating its
1371
1404
  accounting. The failed turn still stores the exact request and never fabricates
1372
1405
  an assistant or a zero-valued observation.
1373
1406
 
1374
- ### §attribution Plugin-authored attribution folksonomy
1407
+ ### §attribution Extension-authored attribution folksonomy
1375
1408
 
1376
- A plugin may declare opaque attribution tags statically or at runtime under the
1377
- shared contract {§plugin-attribution}:
1409
+ An extension may declare opaque attribution tags statically or at runtime under the
1410
+ shared contract {§extension-attribution}:
1378
1411
 
1379
1412
  ```jsonc
1380
1413
  { "plurnk": { "attribution": "@acme/widgets" } } // always-on string or string[]
@@ -1382,14 +1415,14 @@ shared contract {§plugin-attribution}:
1382
1415
 
1383
1416
  | Stage | Contract |
1384
1417
  | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1385
- | Collection | Immediately before each emission attempt, Core pulls the admitted scheme, executor, loaded mimetype-handler, and selected provider sources. A BARE call pulls only its selected provider source because it admits no other plugin capability. |
1418
+ | Collection | Immediately before each emission attempt, Core pulls the admitted scheme, executor, loaded mimetype-handler, and selected provider sources. A BARE call pulls only its selected provider source because it admits no other extension. |
1386
1419
  | Composition | Core flattens, deduplicates, and sorts the tags. The resulting non-empty array rides `generate({ attributions })`; an empty set omits that provider field. |
1387
- | Meaning | Core neither verifies nor infers contribution. Tags are plugin-authored folksonomy for telemetry, optimization, attribution, or downstream rules. The `@plurnk/` reservation is the only namespace policy ({§plugin-attribution}). |
1420
+ | Meaning | Core neither verifies nor infers contribution. Tags are extension-authored folksonomy for telemetry, optimization, attribution, or downstream rules. The `@plurnk/` reservation is the only namespace policy ({§extension-attribution}). |
1388
1421
  | Request evidence | The stored request packet carries the exact set most recently forwarded for that turn. Every generation-kind `inference_calls` row carries that call's exact set, including response-less failures. |
1389
1422
  | Derived reporting | Turn, loop, digest, and client views project the recorded sets. `loop/terminated.attributions` is their deduplicated sorted union and remains separate from provider usage and charge evidence. |
1390
1423
 
1391
- Runtime hooks are synchronous and receive only the attempt coordinates. A hook
1392
- failure is an internal plugin-contract failure; Core does not silently discard
1424
+ Runtime `attributions` functions are synchronous and receive only the attempt coordinates.
1425
+ A function's failure is an internal extension-contract failure; Core does not silently discard
1393
1426
  it or reinterpret a malformed tag list.
1394
1427
 
1395
1428
  §client-metadata **The workspace records which frontend opened it.** A frontend self-identifies (e.g. `@plurnk/plurnk-tui/1.4.0`) at `workspace.create({ settings: { client } })` and the daemon stores it with the workspace. It is validated on write, never forwarded to a provider, and never model-facing. Workspace-stable and self-reported — distinct from attribution's install-grounded tags — and omitted when unset.
@@ -1402,7 +1435,7 @@ it or reinterpret a malformed tag list.
1402
1435
  service-side caching, per-loop selection, context-cap handling, and admission
1403
1436
  of an operator's grammar. Cataloged providers use Models.dev metadata and official AI SDK
1404
1437
  bindings; an operator declaration covers an uncataloged compatible endpoint;
1405
- plugin discovery is the last protocol-extension seam. Cache identity includes
1438
+ a provider extension is the last seam for a new protocol. Cache identity includes
1406
1439
  the alias, wire route, and complete provider-knob projection; a registered
1407
1440
  preconstructed handle occupies that same identity and cannot shadow changed
1408
1441
  tuning.
@@ -1445,7 +1478,7 @@ First path segment = provider name; rest = provider-native model id.
1445
1478
 
1446
1479
  ### Mock provider (sibling fixture)
1447
1480
 
1448
- §mock-provider-mock-fixture `Mock` (exported from `@plurnk/plurnk-providers`) — intg fixture + reference implementation. `{ contextWindow, responses }` constructor; `generate` shifts from the queue. `MockResponse.assistant.ops?: PlurnkStatement[]` is a pre-parsed escape hatch the engine consumes directly when present; production providers don't expose this — and being a plugin export, this contract has no service-side `§`-ref.
1481
+ §mock-provider-mock-fixture `Mock` (exported from `@plurnk/plurnk-providers`) — intg fixture + reference implementation. `{ contextWindow, responses }` constructor; `generate` shifts from the queue. `MockResponse.assistant.ops?: PlurnkStatement[]` is a pre-parsed escape hatch the engine consumes directly when present; production providers don't expose this — and being a package export, this contract has no service-side `§`-ref.
1449
1482
 
1450
1483
  ---
1451
1484
 
@@ -1528,7 +1561,7 @@ Folders and globs select members in those same filesystem coordinates. Parent-di
1528
1561
 
1529
1562
  ### File scheme: creation and misses
1530
1563
 
1531
- §fs-write-surface **The write surface — one admission and incorporation path.** Existing writes remain membership-gated. An absent path additionally crosses the effective creation scope and the complete constraint/Git policy before a proposal is issued. EDIT, COPY destinations, and MOVE destinations use this same path regardless of whether the producer is a model, client, plugin, or `_plurnk`. A COPY or MOVE destination scope on an absent channel resolves against its empty pre-mutation value under {§empty-mutation-scope}; a valid scope creates the channel with the selected source as its complete value. A coordinate outside that empty value is 416. Binary scopes remain numeric byte positions or ranges under {§binary-parity}.
1564
+ §fs-write-surface **The write surface — one admission and incorporation path.** Existing writes remain membership-gated. An absent path additionally crosses the effective creation scope and the complete constraint/Git policy before a proposal is issued. EDIT, COPY destinations, and MOVE destinations use this same path regardless of whether the producer is a model, client, or `_plurnk`. A COPY or MOVE destination scope on an absent channel resolves against its empty pre-mutation value under {§empty-mutation-scope}; a valid scope creates the channel with the selected source as its complete value. A coordinate outside that empty value is 416. Binary scopes remain numeric byte positions or ranges under {§binary-parity}.
1532
1565
 
1533
1566
  | Case | Required admission | Accepted result |
1534
1567
  |------|--------------------|-----------------|
@@ -1583,9 +1616,9 @@ Every fact names the canonical key, never the host root or an echo of the
1583
1616
  model's spelling. These classes let a caller distinguish a wrong address, an
1584
1617
  invalid range, read-only authority, and occupied hidden state without guessing.
1585
1618
 
1586
- §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 member key, never a `file://` URI. Recovery treats path correction with FIND, creation with EDIT, and admission with `members (add)` as alternatives; it must not presume a missing READ requires creation or admission. Admission takes 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, so keep working files inside the root.` 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}).
1619
+ §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 member key, never a `file://` URI. 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. `` Admission takes 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}).
1587
1620
 
1588
- §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, since admitting it is not what the model needs: READ and KILL answer 404, EDIT 403. The detail is `'<key>' is a directory, not a file; <OP> reads/removes/writes one file.` and the recovery names the listing that reaches its files, `` List its files with `FIND (<key>/)`, then READ one by its path. `` (KILL: `then KILL each by its path`; EDIT: `` Name a file inside it, as `EDIT (<key>/<file>)`; list its files with `FIND (<key>/)`. ``). Beyond the root the disk stays dark and {§membership-read-refusal} holds unchanged.
1621
+ §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.
1589
1622
 
1590
1623
  §file-find-directory **FIND recognizes existing directories without requiring a trailing slash.** An exact target naming a directory inside the workspace root resolves to the same recursive collection as its slash-suffixed spelling, including content matching and result pagination. Only workspace members appear; an empty directory or one containing only non-members yields a successful empty survey. Exact files stay exact, genuinely absent paths retain their missing-entry error, and this resolution does not inspect disk paths beyond the root. Shared glob and non-file URI semantics are unchanged.
1591
1624
 
@@ -1857,8 +1890,6 @@ operator knobs in `.env.defaults`. Search indexing performs no inference.
1857
1890
 
1858
1891
  §per-entry-channels-edit-writes-only-body EDIT writes one channel per call — the channel resolved from the path's fragment (or the scheme's `defaultChannel` when no fragment).
1859
1892
 
1860
- No stored `preview` channel — channel content is pulled on READ, never previewed.
1861
-
1862
1893
  Schemes MAY declare multiple channels (`node`: stdout/stderr; `http`: body/header; SSE: per-event-type). Each goes in `manifest.channels` with mimetype pinned; rendered independently. Execution input is control under {§exec-input}, not a stored channel.
1863
1894
 
1864
1895
  For a multi-channel streaming READ, persistence and publication are distinct: the scheme may acquire and persist auxiliary channels, but a fragmentless target publishes only the manifest's `defaultChannel`. An explicit fragment publishes that channel. Thus an ordinary HTTP READ presents the sanitized `body`; response metadata and archival DOM remain addressable implementation/diagnostic surfaces rather than ambient model context.
@@ -2035,7 +2066,7 @@ AST: `{ op: "EDIT", target, body: string | null, signal: tags | null, lineMarker
2035
2066
  - §edit-null-clears Writes the body; `body: null` clears it.
2036
2067
  - §edit-status-201-200 Returns `{ status: 201, entryId }` for a new entry and
2037
2068
  `{ status: 200, entryId }` for a content update.
2038
- - §edit-noop-304 A write that changes nothing — identical content — returns `{ status: 304, entryId }`, mirroring a scoped KILL's idempotence ({§log-kill-scope}). Its terse detail states the observed equality and the valid empty-body deletion shape; it never presumes that repetition or retrieval is the intended recovery.
2069
+ - §edit-noop-304 A write that changes nothing — identical content — returns `{ status: 304, entryId }`, mirroring a scoped KILL's idempotence ({§log-kill-scope}). Its terse detail states the observed equality, `No change: EDIT body matches the selected content.`, and nothing else ({§diagnostic-observation}).
2039
2070
  - §edit-marker-required-on-existing **A markerless EDIT is CREATE-ONLY — there is no easy-clobber path on an existing entry.** A `<L>` marker scopes an EDIT to a range; without one, the body becomes the entry's WHOLE content — legitimate and required for a fresh entry (nothing exists to scope into), but on an EXISTING entry a missing marker is refused **400**, never a silent full replace. A deliberate full rewrite states that intent explicitly: `<1,-1>` resolves through the ordinary marker math to the same whole-content replacement, so the capability is available but cannot be selected by omission.
2040
2071
  - §edit-line-anchors An anchored EDIT resolves under {§line-anchors} and carries
2041
2072
  its endpoint checks as a core-private mutation precondition. Otherwise-valid
@@ -2109,7 +2140,7 @@ READ is the one fan-out core performs ({§read-fan-out}).
2109
2140
  when that differs from the channel's own mimetype the result names the
2110
2141
  channel's as `sourceMimetype`, so a consumer can still run the channel's
2111
2142
  handlers over a whole-resource `<1,-1>` read.
2112
- - §log-range-miss-names-stream A 416 on a log execution item is the range twin of its channel miss ({§log-channel-miss-names-stream}): the coordinate addresses the row's invocation (its authored call body, often empty or one line) while the execution's output stays readable at the stream address the row records. When that stream link exists, the 416 gains it as `stream`, the detail appends where the command's streams live, and `recovery` is `READ <stream> for the command's stream`, whether the invocation's extent is empty or merely shorter than the range (#759). A 416 on a row with no recorded stream stays byte-identical to the generic slicer's.
2143
+ - §log-range-miss-names-stream A 416 on a log execution item addresses the row's invocation (its authored call body, often empty or one line), while the execution's output stays readable at the stream address the row records. When that stream link exists, the 416 carries it as the `stream` extension; its detail and recovery stay the generic slicer's, naming no cause ({§diagnostic-observation}).
2113
2144
  - §read-pattern **A pattern selects the lines a READ renders.** With a heading
2114
2145
  matcher ({§matcher-option} in the contracts SPEC) an exact-target READ stays a
2115
2146
  READ: the matcher runs over the channel's complete text — a regex anchors
@@ -2238,7 +2269,7 @@ AST: `{ op: "KILL", target, matcher: MatcherBody | null, lineMarker: TextLineMar
2238
2269
 
2239
2270
  KILL deletes context from the **log** (`log:///`, {§packet}). Without a scope it retires the selected rows from the active projection ({§log-history-projection}). With a one-line or inclusive two-line scope it removes only that body's intersecting body-relative physical lines from the readable projection, and the row stays active. An anchor may be one published on that body or one returned by READing its `log:///` coordinate ({§line-anchors}); an anchor absent from the current body selects no line, as with an out-of-bounds numeric line. Scoped KILL is one-way: intervals accumulate, the durable body is untouched, and subsequent access follows {§log-readable-projection}. A scoped KILL on a bodyless row is a friendly 200 no-op with `matched` reported. An emission row is curated whole ({§emission-row}): a scope covering every line retires it like an unscoped KILL; on its exact coordinate a narrower scope is 422 `emission-curated-whole`, whose recovery names both forms that retire it; a sweep leaves it intact. A KILL that addresses no row is 404 on an exact coordinate and 204 on a sweep ({§log-curation-folder-idiom}). Selection composes target/glob with an optional heading pattern ({§log-curation-set-selection}). Parameterless KILL instead requests completion ({§kill-conclusion}).
2240
2271
 
2241
- §log-scope-recovery A log-body scope follows the file slicer's range rule ({§range-starts-at-one} in the schemes SPEC): a range starting at 0 — `<0,-1>` included — is refused 416 `range-not-satisfiable` on every body, empty ones too, and never clamped; its detail is the slicer's own sentence, `Range <0,-1> starts at 0, which is not a line; lines are numbered from 1.`, and its recovery names the forms a log body takes — `Write <1,-1> to trim every line of the body; KILL (log:///1/9/2/READ) with no scope retires the whole row.`, or `To trim lines 1 through M, write <1,M>; …` — never the insert and append positions a body cannot take. Every other scope that names no line is 400 `curation-scope-invalid` and likewise names the model's mistake in its coordinates and the forms that work on that row: `<0>` offers `<1>`; an end below 1 offers `<L,-1>`; a backward `<5,3>` offers `<3,5>`; anything else offers `<L>`, `<L,M>` and the unscoped row KILL.
2272
+ §log-scope-recovery A log-body scope follows the file slicer's range rule ({§range-starts-at-one} in the schemes SPEC): a range starting at 0 — `<0,-1>` included — is refused 416 `range-not-satisfiable` on every body, empty ones too, and never clamped; its detail is the slicer's own sentence, `Range <0,-1> starts at 0, which is not a line; lines are numbered from 1.`, and its recovery names the forms a log body takes — `Trim one line with <L> or lines L through M with <L,M>; KILL (log:///1/9/2/READ) with no scope retires the whole row.` — never the insert and append positions a body cannot take. Every other scope that names no line is 400 `curation-scope-invalid` with the same recovery: the forms that work on that row, never a rebuild of the written scope ({§diagnostic-observation}).
2242
2273
 
2243
2274
  A READ carrying active native media is atomic: any KILL scope is ignored and the entire observation is retired, including its native context contribution ({§packet-attachment-parts}). For a model turn, native activity is the attachment selection in its actual input packet; without a model packet, a native observation is atomic by default. Text-only observations in the same selection retain ordinary scoped behavior. Neither form deletes source data or forensic evidence.
2244
2275
 
@@ -2246,7 +2277,7 @@ A READ carrying active native media is atomic: any KILL scope is ignored and the
2246
2277
 
2247
2278
  | Fact | Owner | Effect |
2248
2279
  | --- | --- | --- |
2249
- | Initial body suppression | Immutable event `initial_folded` | Packet presentation only; explicit retrieval can read an initially hidden body. An emission row's hidden body reaches the packet as its assistant message ({§emission-row}). |
2280
+ | Initial body suppression | Immutable event `initial_folded` | Packet presentation only; explicit retrieval can read an initially hidden body. An emission row remains folded; its program is separately eligible for {§previous-emission}. |
2250
2281
  | Deliberate scoped KILL | Current projection `folded`, initially empty | Packet, READ, FIND, COPY, and search omit those lines; later retrieval cannot undo trimming. |
2251
2282
 
2252
2283
  Packet display combines both masks. Other consumers use only deliberate trimming.
@@ -2284,7 +2315,7 @@ The `## Log` section is a sequence of ordinary Markdown records separated by one
2284
2315
  | facts | One strict JSON object in stable alphabetical order. | Present only when a fact exists. Asides, scopes, opaque invocation metadata and result facts belong here, not on the H3. |
2285
2316
  | body | Coordinate-prefixed lines. | Present when the row is visible. |
2286
2317
 
2287
- An emission row's body is not in its record: it follows the record as the worker's assistant message ({§packet-wire-envelope}). 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.
2318
+ An emission row's body is not in its record ({§emission-row}); the optional previous program follows the user footer ({§previous-emission}). 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.
2288
2319
 
2289
2320
  §log-address-metadata **Addresses name their relationship, not the row's producer.**
2290
2321
 
@@ -2366,7 +2397,7 @@ Authored `metadata` retains its opaque ordered block strings under {§scheme-met
2366
2397
  records the exact READ coordinates sent without controlling retention. Missing immutable bytes are an
2367
2398
  internal integrity failure, never silently dropped content. No ejection message or permanent teaching is
2368
2399
  added. These stable curation weights are not provider-token measurements ({§tokenomics-render-weight-budget}).
2369
- - §packet-token-accounting Every row reports one `tokens` charge on its H3 ({§log-wire-format}): its complete materialized H3, facts, visible body, selected native attachment, and the emission it delivers outside its record ({§emission-row}). The completed record is measured to a fixed point, including the accounting field itself. No `tokensBody`, `tokensMetadata`, or `tokensActive` field is serialized. Hidden text is not charged; metadata-only rows still have a reclaimable charge. Source/FIND-item `tokens` measure source content, not the observation's context footprint. A FIND's nonzero `itemsTokenTotal` weighs the complete matched set; a nonzero `returnedItemsTokenTotal` appears only when the returned page differs. All use stable curation weights, not provider tokens or dollars. Native component accounting follows {§packet-attachment-parts}; ordinary addressability and truthful errors follow {§log-wire-format}.
2400
+ - §packet-token-accounting Every row reports one `tokens` charge on its H3 ({§log-wire-format}): its complete materialized H3, facts, visible body and selected native attachment. The previous-emission section is charged separately as user-slot text ({§previous-emission}). The completed record is measured to a fixed point, including the accounting field itself. Hidden text is not charged; metadata-only rows still have a reclaimable charge. Source/FIND-item `tokens` measure source content, not the observation's context footprint. A FIND's nonzero `itemsTokenTotal` weighs the complete matched set; a nonzero `returnedItemsTokenTotal` appears only when the returned page differs. All use stable curation weights, not provider tokens or dollars. Native component accounting follows {§packet-attachment-parts}; ordinary addressability and truthful errors follow {§log-wire-format}.
2370
2401
 
2371
2402
  ### §retrieval-packet-metadata READ/FIND packet metadata
2372
2403
 
@@ -2416,37 +2447,37 @@ single line past the end, a reversed range, empty content, a command's log row
2416
2447
 
2417
2448
  ### §emission-row The emission row
2418
2449
 
2419
- Every admitted emission is announced by one row of its own turn, so the transcript carries the
2420
- worker's emissions in chronological place ({§packet-wire-envelope}) and the worker curates them
2421
- like any other row.
2450
+ Every admitted content emission is announced by one curatable row of its own turn.
2451
+ The most recent eligible program supplies {§previous-emission}; older programs remain readable.
2422
2452
 
2423
2453
  | Surface | Contract |
2424
2454
  |---|---|
2425
2455
  | When | An inference turn that admitted at least one content statement. Reasoning-only turns, including initialization ({§worker-initialization-entry}), a programmatic batch, an empty turn ({§empty-turn}), a client operation and a rejected attempt ({§rejected-emission-entry}) announce nothing. |
2426
2456
  | Place | After the turn's inputs (arrivals, deltas, open-path READs) and before its reasoning NOTEs and operations, written after the selection snapshot ({§turn-ops-selection-snapshot}): the emission sits between what the worker had seen and what it caused. |
2427
2457
  | Row | A `_plurnk` READ of the turn's own source, `ops://<worker>/L/T`, with `attrs.kind="emission"` and the canonical leaf `/emission`: `### log:///L/T/S/emission → ops://<worker>/L/T · N`. It renders its author, the turn's producer, as `origin`, so a model's row carries none. It is no operation: no receipt, tool call or strike, and outside the op mix. |
2428
- | Body | Frozen at announcement: the canonical rendering ({§statement-rendering}) of admitted content statements, each in a closed fence. Each body appears as its head, about a hundred tokens of curation weight — enough to recognise the turn, never enough to matter to the budget ({§context-fit}) — and a longer body is that head with no mark inside the fence, its closing fence carrying `<!-- preview; the whole statement ran: READ (ops://<worker>/L/T) for all of it -->`. It is a preview, never a truncation: a mark of the cut in the model's own assistant message reads as a statement that went out truncated, and the model copies it as its own vocabulary (#1006). Nothing the harness writes enters a fence. Absent and empty bodies remain empty. All heading operands, scopes, metadata, patterns and asides remain. Free text and unadmitted forms are absent; a recovered native call ({§native-tool-calls}) appears as the operation it was read as; an operation whose receipt failed stays. Reasoning operations retain their own source and normal receipts, never an assistant-content copy. No body text is inspected for nested operations. |
2429
- | Sources and memory | Dispatch and immutable `ops://` sources retain complete bodies ({§turn-source-resources}); an explicit source READ returns them normally. The wire omits NOTE and WAIT blocks, whose own rows show them whole ({§body-projection}), so curating a NOTE row removes its text; an emission of only NOTE and WAIT delivers nothing, its row stands, and no assistant message follows it ({§packet-wire-envelope}); reasoning-only NOTEs never enter this projection. |
2458
+ | Body | Frozen at announcement: the canonical rendering ({§statement-rendering}) of the admitted content statements, each whole in a closed fence. All heading operands, scopes, metadata, patterns and asides remain. Free text and unadmitted forms are absent; a recovered native call ({§native-tool-calls}) appears as the operation it was read as; an operation whose receipt failed stays. Reasoning operations retain their own source and normal receipts, never an assistant-content copy. No body text is inspected for nested operations. |
2459
+ | Wire | The record appears in the log; its complete program is eligible for {§previous-emission}. No operation family or body is filtered. Dispatch and the immutable `ops://` source keep complete bodies ({§turn-source-resources}); an explicit READ returns them. |
2430
2460
  | Stability | The projection is fixed from its first appearance, never aged or resized under budget pressure. Already frozen announcements and historical request captures are not rewritten. |
2431
- | Presentation | Born folded: the record shows its header, and its body follows the record as the worker's assistant message. |
2432
- | Accounting | `tokens` charges the record and the emission the wire delivers, preview notes included, never the omitted NOTE and WAIT blocks or the cut remainder ({§packet-token-accounting}). An explicit source READ has its own ordinary charge. |
2461
+ | Presentation | Born folded: the log record shows its header, never an assistant message. |
2462
+ | Accounting | The row's `tokens` charges only its rendered record. The optional previous-emission section is measured once as user-slot text ({§packet-token-accounting}); replacing it never changes older row weights. An explicit source READ has its own ordinary charge. |
2433
2463
  | Curation | Curated whole ({§log-kill-scope}): KILL retires it, and so does a scope covering every line (`<1,-1>`); on its exact coordinate a narrower scope is 422 `emission-curated-whole`, and a sweep whose scope would only trim it leaves it intact. |
2434
- | Schema | Migration 12 admits `kind="emission"` only on this shape: one per turn, the turn's newest row when written, frozen, and curated whole. A database from before version 12 keeps its rows and gains no announcement. FORK copies it with the inherited turns, still naming its writer. |
2435
- | Echoes | A worker that repeats the heading in its own text is tolerated ({§fabricated-log-entry}); the digest counts the echoes. A worker that copies the preview note onto its own closer keeps its body intact; the note is outside text ({§outside-text}). |
2464
+ | Schema | Migration 12 admits `kind="emission"` only on this shape: one per turn, the turn's newest row when written, frozen, and curated whole; it backfills no announcement for earlier turns. FORK copies it with the inherited turns, still naming its writer. |
2465
+ | Echoes | A worker that repeats the heading in its own text is tolerated ({§fabricated-log-entry}); the digest counts the echoes. |
2436
2466
 
2437
2467
  ### §reasoning-row The reasoning row
2438
2468
 
2439
2469
  The model's memory of its own reasoning, behind `PLURNK_SERVICE_REASONING_ROWS` (default `1`):
2440
2470
  when it is `1`, every model turn that reasoned lands its reasoning as one row of its
2441
2471
  own, `log:///<loop>/<turn>/<sequence>/reasoning → reasoning://<worker>/<loop>/<turn>`, the harness's
2442
- READ of the turn's reasoning source, immediately before the turn's emission row, so the transcript
2443
- reads what the model thought and then what it emitted, and the assistant message that follows stays
2444
- the canonical program ({§emission-row}, {§packet-wire-envelope}).
2472
+ READ of the turn's reasoning source, immediately before the turn's emission row (or its first
2473
+ operation or diagnostic when there is no content emission), so the transcript
2474
+ reads what the model thought and where its content program can be found
2475
+ ({§emission-row}, {§packet-wire-envelope}).
2445
2476
 
2446
2477
  | Concern | Rule |
2447
2478
  |---|---|
2448
- | Room | The row lands only when the packet has room for its page after everything else the turn lands, one receipt's reserve kept for the emission row's head ({§context-fit}); without room, no row lands and nothing else in the turn changes: no prefix, no receipt, no other row touched. |
2449
- | Body | The model's own text when it fits the row's page — `PLURNK_SERVICE_REASONING_TRAILING_LINES` lines, unset meaning the shared `PLURNK_SERVICE_PREVIEW_LINES`, within `PLURNK_SERVICE_PREVIEW_CHARS` — and otherwise its **last page**: the final lines, trimmed from the front on a line boundary, because a turn's conclusions and plan sit at its end (#999). The statement carries the scope it took and the receipt its `range` ({§packet-extent-metadata}); the complete reasoning stays readable at its address by any scope ({§turn-source-resources}). |
2479
+ | Room | Admission occurs after all mandatory turn rows, including the emission and diagnostics, using the remaining room under {§context-fit}. Its earlier chronological coordinate is reserved without inserting a placeholder; if it does not fit, that coordinate remains unused. Nothing else changes: no partial preview, refusal, or other row touched. Reasoning-only and empty model turns follow the same rule; content operations are not required. |
2480
+ | Body | The model's own text when it fits the row's page — `PLURNK_SERVICE_REASONING_TRAILING_LINES` lines, unset meaning the shared `PLURNK_SERVICE_PREVIEW_LINES`, within `PLURNK_SERVICE_PREVIEW_CHARS` — and otherwise its **last page**. Keep complete final lines where possible; if the last line alone exceeds the character bound, select its suffix with the ordinary exact column scope. Unicode code points count once and CRLF is one indivisible separator, as in {§body-projection}. The statement carries the scope it took and the receipt its `range` ({§packet-extent-metadata}); the complete reasoning stays readable at its address by any scope ({§turn-source-resources}). |
2450
2481
  | Weight | Charged like any row, visible in the gauge and its `largest` ({§context-gauge}). |
2451
2482
  | Curation | The verbs alone: a KILL retires it like any row ({§context-verbs}); the daemon reorders nothing and un-bodies nothing on its account. |
2452
2483
  | Absence | Means no room or no reasoning; the model tells them apart by reading `reasoning://` for the turn. |
@@ -2456,7 +2487,7 @@ the canonical program ({§emission-row}, {§packet-wire-envelope}).
2456
2487
  | Surface | Contract |
2457
2488
  |---|---|
2458
2489
  | Identity | `ops://<worker>/<loop>/<turn>`, `reasoning://<worker>/<loop>/<turn>`, and `note://<worker>/<loop>/<turn>/<item>` name a worker in the current workspace and its durable coordinates. A note's item is its dispatched NOTE ordinal. The `outside` source ({§outside-text}) has no address: no `outside://` scheme exists, and it is reached only through `outside/event`, FORK and the digest. The worker authority is required and case-sensitive; userinfo, ports, and queries are invalid. Source identity never depends on the reading worker. `log:///` remains local; READ, FIND and KILL reject log authorities, userinfo, ports and queries with 400, never substitute the caller's log. |
2459
- | Source | `ops` is exact admitted `text/vnd.plurnk`, and its turn's emission row carries the preview-bounded projection ({§emission-row}); `reasoning` is `text/plain` containing the selected original provider reasoning or a non-model producer's authored rationale. Producer identity comes from the owning turn; a harness rationale is not provider evidence. The turn decides existence and the source decides content: a turn that exists but has no source of that kind reads as the ordinary empty resource (204, empty body), never a fabricated one; a worker or turn that does not exist is 404. |
2490
+ | Source | `ops` is exact admitted `text/vnd.plurnk`, and its turn's emission row carries its canonical projection ({§emission-row}); `reasoning` is `text/plain` containing the selected original provider reasoning or a non-model producer's authored rationale. Producer identity comes from the owning turn; a harness rationale is not provider evidence. The turn decides existence and the source decides content: a turn that exists but has no source of that kind reads as the ordinary empty resource (204, empty body), never a fabricated one; a worker or turn that does not exist is 404. |
2460
2491
  | Notes | Each dispatched NOTE stores its exact literal body as an immutable `text/plain` source and returns its worker-qualified address. There may be multiple notes in a turn, from reasoning, content, or another producer. A missing note is 404, not an empty invented note. Sharing its URI uses ordinary SEND; the receiver deliberately READs it. NOTE itself sends no ambient update. |
2461
2492
  | Retention | One ops source, one reasoning source and one outside source per turn; one note source per NOTE ordinal. An optional inference-call link records provenance. Source removal follows deletion of its owning turn, never log curation. |
2462
2493
  | Operations | Ordinary scoped READ, FIND, content search and COPY from any named worker's source within the workspace. FIND accepts authority and path patterns, retaining complete worker-qualified identities in results and folder selectors. READ returns data and never executes it. Sources are read-only for every actor and have no edit hashes. |
@@ -2467,7 +2498,7 @@ the canonical program ({§emission-row}, {§packet-wire-envelope}).
2467
2498
  §rejected-emission-entry A rejected provider response is not `turnOps`: it never became an admitted turn program, and no emission row announces it ({§emission-row}). The one bounded invalid-emission recovery item under {§emission-admission} has `attrs.kind="emissionAttempt"`, `origin="model"`, the canonical model-facing `/attempt` leaf, and the exact latest rejected response. The packet does not duplicate that identity as `kind` metadata. It is born durably body-suppressed and projected visibly only in the informed recovery packet; every other rejected attempt remains forensic-only.
2468
2499
 
2469
2500
  - §log-coordinate-hierarchy **Log coordinates are a hierarchical prefix; the trailing slash is optional** — a coordinate is `loop/turn/sequence`, and a PARTIAL coordinate selects its descendants: `log:///1` = loop 1's rows, `log:///1/2` = turn 1/2's rows, `log:///1/2/3` = the one row. A full coordinate is always three parts, so a one- or two-part path is unambiguously a prefix — the trailing slash is an optional alias (`log:///1/2` ≡ `log:///1/2/`), uniform with ```` ```READ (worker:///docs/) ````. A complete `[start-end]` segment in any numeric coordinate slot selects that inclusive decimal interval; brackets elsewhere retain ordinary path-glob meaning. Every rendered row appends one canonical model-facing leaf: the native operation name or invoked executor name, `/attempt` for a rejected emission, `/emission` for an admitted one's announcement ({§emission-row}). An executor leaf is derived from the durable submitted statement (its `runtime`), never an internal dispatch type or the current tool registry. Digits and punctuation in executor names remain part of the leaf. The leaf names identity rather than adding a resource level. Exact consumers tolerate the unsuffixed three-part shorthand; when supplied, the case-insensitive leaf is authoritative and a disagreement resolves 404. READ anchors use the canonical suffixed identity even when addressed by shorthand. Typed entry materialization therefore resolves as `/READ` while retaining its durable `EDIT` event ({§exec-entry-sink}). `log:///1/2/*` still selects the turn's item rows, while `log:///**/READ`, `log:///**/python3`, `log:///**/attempt`, and `log:///**/emission` deliberately filter canonical leaves. Executor outputs instead use workspace-wide claims such as `sh:///ab3d5678#stdout` ({§execution-output-identity}); their source operation has log coordinates, but resource lifetime and identity are independent of that observation. Error pointers, Problem instances, source attribution, and search use this same identity; client stream coordinates retain the numeric triple. Within a turn, sequence is arrival order. Inbound SEND rows publish before the program runs ({§message-arrival}); a turn receiving messages holds the first at `log:///L/T/1/SEND`, followed by further arrivals oldest first, then its emission's announcement, then the model's operations ({§packet-current-turn} names `L/T`).
2470
- - §log-near-miss **A log miss beside rows that exist names them.** A READ, FIND or KILL of an exact coordinate that names no row carries a recovery from the worker's own active rows: the row at that coordinate under its own leaf (`` The entry at log:///1/25/8 is `log:///1/25/8/sh`. ``), else the turn's rows carrying the leaf written, else the turn's rows, at most six named, else the loop's latest turn. A coordinate in a loop with no rows carries none. Recorded misses were near misses every time: a READ of `1/25/8/READ` when the row was `sh`, an EDIT one sequence off, and a KILL under the 413 that must curate (#1005).
2501
+ - §log-near-miss **A log miss beside rows that exist names them.** A READ, FIND or KILL of an exact coordinate that names no row carries a recovery from the worker's own active rows: the turn's rows carrying the leaf written come first, since the leaf is what the model asked for and the sequence its guess, followed by the row at that coordinate under its own leaf (`` Turn 1/44's reasoning is `log:///1/44/2/reasoning`; the entry at log:///1/44/1 is `log:///1/44/1/READ`. ``); with no row of that leaf, the row at the coordinate alone (`` The entry at log:///1/25/8 is `log:///1/25/8/sh`. ``), else the turn's rows, at most six named, else the loop's latest turn. A coordinate in a loop with no rows carries none. Recorded misses were near misses every time: a READ of `1/25/8/READ` when the row was `sh`, an EDIT one sequence off, and a KILL under the 413 that must curate (#1005).
2471
2502
  - §log-curation-folder-idiom **Log curation speaks the folder idiom; a zero-match sweep is a no-op success** — KILL takes a concrete coordinate or a path-glob, and a **trailing slash or a partial coordinate means "the contents"** ({§log-coordinate-hierarchy}), like a folder-scoped FIND: ```` ```KILL (log:///1/2) <1,-1> ```` suppresses turn 1/2's bodies and retires its emission ({§emission-row}). A **well-formed selection that matches nothing is 204 with `matched: 0`**; a successful sweep's rx carries `matched: N`. Parameterless KILL instead requests completion ({§kill-conclusion}).
2472
2503
  - §log-curation-set-selection **Row selection and body scope are independent** — target/glob and an optional heading pattern (```` ```KILL (log:///**) [{"pattern": "~stale"}] ````, every dialect a FIND over rows accepts) compose by intersection into the affected row set. An optional `<L>` or `<SL,EL>` then intersects each selected canonical body; it never paginates or changes the selected set. An emission row is curated whole instead: the scope retires it when it covers every line and otherwise leaves it ({§emission-row}). Thus ```` ```KILL (log:///**/READ) <17,-1> ```` may change long READs and no-op on short ones while reporting every selected row in `matched`.
2473
2504
 
@@ -2687,12 +2718,12 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
2687
2718
 
2688
2719
  ### §find FIND
2689
2720
 
2690
- - §log-uniform-query **Log speaks the universal query contract** — ```` ```FIND (log://…) ```` works like every scheme's FIND. Candidates are worker rows scoped by the coordinate hierarchy ({§log-coordinate-hierarchy}) and projected exactly as READ shows them. Content dialects use `Matcher.matchCandidates`; `~` full-text and `&graph` use the same persistent derivation artifacts and candidate rankers as entries. Broad results are one-channel catalog groups whose `[0].path` is `log:///loop/turn/seq/OP`; exact matcher results are flat locations ({§find-result-projection}). Log remains the core event ledger rather than duplicating rows into `entries`; its core-private storage adapter supplies one complete channel representation to the same READ projector. That adapter is not a plugin seam and grants no protocol scheme an alternate READ path.
2721
+ - §log-uniform-query **Log speaks the universal query contract** — ```` ```FIND (log://…) ```` works like every scheme's FIND. Candidates are worker rows scoped by the coordinate hierarchy ({§log-coordinate-hierarchy}) and projected exactly as READ shows them. Content dialects use `Matcher.matchCandidates`; `~` full-text and `&graph` use the same persistent derivation artifacts and candidate rankers as entries. Broad results are one-channel catalog groups whose `[0].path` is `log:///loop/turn/seq/OP`; exact matcher results are flat locations ({§find-result-projection}). Log remains the core event ledger rather than duplicating rows into `entries`; its core-private storage adapter supplies one complete channel representation to the same READ projector. That adapter is not an extension seam and grants no protocol scheme an alternate READ path.
2691
2722
  - §find-source-agnostic **The content matcher is source-agnostic** — `Matcher.matchCandidates(body, candidates, mimetypes)` applies a content matcher (regex/jsonpath/xpath/glob) to candidates from ANY source, keyed by the caller's own identity (a pathname for entries, a `loop/turn/seq` coordinate for log). The matcher never cares what table the content came from, so FIND works uniformly across schemes by construction: `EntryFind` and `Log.find` run the one shared primitive rather than re-implementing it per scheme. Log stays its own event stream, but its rows are candidates the shared matcher covers like any entry's content.
2692
2723
  - §find-line-anchors **A FIND regex anchors each line**, as READ, EDIT and KILL do ({§read-pattern}, {§edit-pattern}): `^` and `$` are a line's ends in every FIND content match — over entries, log rows, turn sources and a binary channel's bytes — so ```` ```FIND (django/urls/resolvers.py) /^from|^import/ ```` locates the same import lines a READ with that pattern shows, never a false 204.
2693
2724
  - §find-candidate-containment **One candidate's crash is that candidate's problem** — arbitrary member content can crash a mimetype handler mid-match (an unbalanced template partial crashed Readability and killed a 1,916-file FIND as a blank 500, #449). `Matcher.matchCandidates` contains a per-candidate handler throw: the candidate drops out exactly like unsupported content, the cause goes to daemon stderr, and only a FIND whose every candidate crashed reports a 415 whose Problem names the first crashing member and handler. The operation's other candidates always answer.
2694
2725
 
2695
- - §find-scope-prefix-filter Filters entries within scope. A **bare** path is the exact entry unless the file scheme resolves a directory under {§file-find-directory}; an explicit **shell glob**, classified once by {§path-glob}, expands to a scope. Path globs use segment semantics: `*` and `?` never cross `/`; `**` does — in every spelling: a `**` glued to a name (`**.go`, `src/**.ts`) is matched as `**/*.go` / `src/**/*.ts`, never demoted to a one-level `*` the way a native matcher reads it. Terminal `*` and `**` are structural catalog selectors and include dot-prefixed entries, so a complete map does not hide `.env.defaults` or `.github`; richer patterns retain native shell behavior. SQLite prefix queries may reduce the candidate set but never decide the match. A trailing slash is a recursive FIND scope only for a scheme whose manifest declares `folderScopes: true`; otherwise it is ordinary resource syntax. This is an explicit plugin contract, never inferred from URL punctuation.
2726
+ - §find-scope-prefix-filter Filters entries within scope. A **bare** path is the exact entry unless the file scheme resolves a directory under {§file-find-directory}; an explicit **shell glob**, classified once by {§path-glob}, expands to a scope. Path globs use segment semantics: `*` and `?` never cross `/`; `**` does — in every spelling: a `**` glued to a name (`**.go`, `src/**.ts`) is matched as `**/*.go` / `src/**/*.ts`, never demoted to a one-level `*` the way a native matcher reads it. Terminal `*` and `**` are structural catalog selectors and include dot-prefixed entries, so a complete map does not hide `.env.defaults` or `.github`; richer patterns retain native shell behavior. SQLite prefix queries may reduce the candidate set but never decide the match. A trailing slash is a recursive FIND scope only for a scheme whose manifest declares `folderScopes: true`; otherwise it is ordinary resource syntax. This is an explicit extension contract, never inferred from URL punctuation.
2696
2727
 
2697
2728
  Resource-authority globs select authorities independently of the path scope.
2698
2729
  Matching resources retain their full addresses through pattern matching,
@@ -2707,7 +2738,7 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
2707
2738
  matches the selected channel's content or derivation; path globs select
2708
2739
  resources through `(target)` ({§path-glob}).
2709
2740
  - §find-fulltext-selection Every matcher operates only over the candidate set selected by `(target)`; indexed matchers do not bypass that selection. `~query` passes the native FTS5 expression to SQLite and ranks matching candidates by ascending BM25, with resource identity breaking ties. Native BM25 uses the shared index's term statistics; candidate visibility, owner, channel and target filters determine which resources can be returned. The ordinary FIND pager selects resources for broad targets or match locations for exact targets: markerless search uses {§markerless-first-page}, `<N>` selects position N and `<N,M>` selects an inclusive range. Fractions are invalid result coordinates, not similarity thresholds. Results expose addressable matched text regions; neither cosine scores nor percentage similarity is invented. Native query-syntax failures return 400 with SQLite's diagnostic; database and implementation failures propagate.
2710
- - §fts-word-phrase **A word with inner punctuation is the phrase of its tokens.** FTS5 barewords hold only letters, digits, `_` and non-ASCII, so before the query reaches SQLite each word outside a quoted string or `NEAR(…)` group that is not a bareword (with optional leading `^` and trailing `*`) is quoted as a phrase: `~inherited-members` searches `"inherited-members"` — the adjacent tokens `inherited members` — instead of failing as `no such column: members`, and `c++`, `x.y`, `a/b` likewise. FTS5's own syntax passes untouched: `AND`/`OR`/`NOT`/`NEAR`, `+`, quoted phrases, parentheses, and column filters (a word containing `:`, `{` or `}`, or opening with `-`). A column-filter failure keeps SQLite's diagnostic and its recovery says what the filter is and gives the bare-word and `NOT` forms: `` `members:` and `-members` are FTS5 column filters, and the index has one column; to search for a word write it bare, as `~members`, and to exclude one write `NOT` between terms, as `~a NOT members`. `` A syntax error over parentheses or quotes, which FTS5 reads as its own syntax, recovers to the phrase and to the regex for the same characters: `` FTS5 reads parentheses and quotes as query syntax: quote the phrase, as `~"all("`, or match the characters with a regex, as `/all\(/`. `` (#1005).
2741
+ - §fts-word-phrase **A word with inner punctuation is the phrase of its tokens.** FTS5 barewords hold only letters, digits, `_` and non-ASCII, so before the query reaches SQLite each word outside a quoted string or `NEAR(…)` group that is not a bareword (with optional leading `^` and trailing `*`) is quoted as a phrase: `~inherited-members` searches `"inherited-members"` — the adjacent tokens `inherited members` — instead of failing as `no such column: members`, and `c++`, `x.y`, `a/b` likewise. FTS5's own syntax passes untouched: `AND`/`OR`/`NOT`/`NEAR`, `+`, quoted phrases, parentheses, and column filters (a word containing `:`, `{` or `}`, or opening with `-`). An FTS5 failure keeps SQLite's own message as its `diagnostic`, and its recovery is the dialect's form, never a rewrite of the query ({§diagnostic-observation}): `` An FTS5 query is barewords, "quoted phrases", and AND, OR, NOT and NEAR between them. ``
2711
2742
  - §find-scoped-isolation Workspace + scheme scoped — no cross-workspace/cross-scheme leakage.
2712
2743
  - §find-result-projection **The resolved target selection determines the result unit; result cardinality never changes it** ({§find-result-unit}). Returns `FindResult { status, content, mimetype, results, range, matchingPathCount, matchLocationCount, itemsWeightTotal, returnedItemsWeightTotal }`:
2713
2744
 
@@ -2742,7 +2773,7 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
2742
2773
  derivation's `{§mimetype-summary}`. Prose is clipped to at most `PLURNK_SERVICE_CATALOG_SUMMARY_CHARS` Unicode
2743
2774
  code points including a visible terminal ellipsis, so a row stays one line
2744
2775
  of orientation; an invocation-form witness — a summary that is one fenced
2745
- operation, the shape every tool family's summary takes
2776
+ operation, the shape a runtime's tool summary takes
2746
2777
  ({§tools-resource-materialization}) — is shown whole, every tool named,
2747
2778
  because the discovery row exists so the model can invoke without a READ and
2748
2779
  a menu that is cut is no menu. Absent metadata is omitted.
@@ -2849,6 +2880,8 @@ accounting and model-visible failure evidence remain separately owned by
2849
2880
  contains exactly one KILL without a target, scope, matcher or metadata, no hard
2850
2881
  parse error or lost boundary, and was not cut at the provider's output allowance.
2851
2882
  The operation limit must admit the entire program.
2883
+ The parser retains everything after that KILL heading as its answer region
2884
+ ({§terminal-kill}); apparent operations there are body text, not siblings.
2852
2885
  SEND, NOTE and log-targeted KILL may accompany it, as may outside text ({§outside-text});
2853
2886
  every other operation requires continuation. This tolerance is unadvertised:
2854
2887
  model teaching requests KILL alone. Reasoning-side NOTEs remain ordinary notes.
@@ -2968,7 +3001,7 @@ of which Worker launched it ({§execution-output-identity}).
2968
3001
  | 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. |
2969
3002
  | Settlement or teardown | Retire and abort input immediately when the execution settles or is cancelled. Existing stream completion and worker wake mechanics remain the only completion path. |
2970
3003
 
2971
- Stored runtime entries retain plugin-only writers. Input and termination are
3004
+ No operation writes a stored runtime entry: its scheme's `writableBy` is empty, and the execution writes its own streams. Input and termination are
2972
3005
  control capabilities, not exceptions granting write access to stdout/resources.
2973
3006
 
2974
3007
  ### §exec Executions
@@ -2987,21 +3020,18 @@ target scheme, before effect admission or source acquisition. Core validates the
2987
3020
  preparation result and retains its cwd; it neither parses option names nor
2988
3021
  redirects metadata to a source scheme. An executor without preparation accepts
2989
3022
  no metadata. A `script`-kind target ({§executor-invocation}) is the program: core inspects it before
2990
- anything spawns — a file is the script; a directory is refused `400 target-not-a-program`,
2991
- pointing at `[{"cwd": "…"}]`; an absent path is refused `400 target-not-found`, giving the
2992
- applicable accepted form without inferring what the model meant. When the target is a
2993
- registered tool of another executor, recovery gives that tool's exact bracketed
2994
- invocation; when it names another available executor (`sh (python3)` over a Python body, #895),
2995
- recovery names that executor's fence — `` `python3` is its own executor; use that name on the
2996
- opening fence and put the program in the body. ``; otherwise it points at an existing script or a bare shell-command body. A non-file resource
3023
+ anything spawns — a file is the script; a directory is refused `400 target-not-a-program`;
3024
+ an absent path is refused `400 target-not-found`, stating what exists and inferring nothing
3025
+ ({§diagnostic-observation}): a registered tool of another executor is named with that executor,
3026
+ another available executor as its own executor (`` `python3` is its own executor. ``), and
3027
+ anything else as a target that must name an existing program resource. A non-file resource
2997
3028
  target that cannot be read keeps the owning READ's failure identity (#163) and states
2998
- the slot contract in its recovery — the resource is the program and the body its stdin;
2999
- a command belongs beneath a targetless heading — without guessing which was meant (#425). The started receipt always
3029
+ the slot contract in its recovery: the resource is the program and the body its stdin. The started receipt always
3000
3030
  names the working directory only when it is not the project root, and then in the
3001
3031
  project-relative form ({§fs-namespace}); the default directory is omitted rather
3002
3032
  than repeated in every receipt. Native file addresses resolve from that same project
3003
3033
  directory, while absolute input retains its filesystem meaning. The `(path)` is a program — a script for an interpreter, a tool name for a tool
3004
- family — and neither a command nor a working directory is ever a target. The default
3034
+ runtime — and neither a command nor a working directory is ever a target. The default
3005
3035
  shell is written as its own fence, ```` ```sh ````; no runtime-less form exists.
3006
3036
 
3007
3037
  §exec-target-near-miss Two target near-misses have one reading each and are read
@@ -3015,15 +3045,14 @@ real stream id is always the program source.
3015
3045
 
3016
3046
  §exec-target-documentation **Generated reference is never a program.** A resource target under
3017
3047
  `worker:///_plurnk/` — the executor and scheme documentation the harness generates — is refused
3018
- at admission, 400 `target-is-documentation`, before any source is realized or run: `` `worker:///_plurnk/plurnk/sh.md` is reference documentation the harness generated, not a program; sh cannot run it. `` With a body the recovery is `Drop the target and keep the command: the opening fence line is sh alone, with the command lines beneath it.`; without one it points to READ for the documentation and to the program's own path or a targetless heading to run something. Admitting it realized the markdown as a host temporary file that a sandboxed runtime could not open (`cannot open /tmp/plurnk-exec-….md`), and ran markdown where it could.
3048
+ at admission, 400 `target-is-documentation`, before any source is realized or run: `` `worker:///_plurnk/plurnk/sh.md` is reference documentation the harness generated, not a program; sh cannot run it. `` It carries no recovery ({§diagnostic-observation}). Admitting it realized the markdown as a host temporary file that a sandboxed runtime could not open (`cannot open /tmp/plurnk-exec-….md`), and ran markdown where it could.
3019
3049
 
3020
3050
  §exec-tool-fall-through **A tool run as a shell command is named at the failure
3021
3051
  site.** A bare shell command whose program is the name of a tool published by
3022
3052
  another enabled runtime (`brave_web_search {…}` under the default shell) exits
3023
- 127; the stream's terminal receipt then says the program is not a shell command
3024
- but a tool of that runtime, gives the exact bracketed invocation with the JSON
3025
- body convention, names the tool's own document, and carries `toolRuntimes` and
3026
- `tool`. The status stays the shell's 500, nothing is rerouted, and a program the
3053
+ 127; the stream's terminal receipt then says the program is a tool of that
3054
+ runtime, names the tool's own document, and carries `toolRuntimes` and `tool`;
3055
+ it builds no invocation ({§diagnostic-observation}). The status stays the shell's 500, nothing is rerouted, and a program the
3027
3056
  registry does not know keeps the plain exit-127 receipt.
3028
3057
 
3029
3058
  | Declared target kind | Authored target | Canonical effect target | Executor realization |
@@ -3046,7 +3075,7 @@ effect classification or proposal creation.
3046
3075
  A runtime with {§executor-tool-registry} admits only the snapshot's exact
3047
3076
  literal targets. An absent target is 400; a target outside that closed enabled
3048
3077
  set is 404; neither reaches effect classification or proposal creation. The
3049
- selected entry's invocation—not the family's structural fallback—owns body
3078
+ selected entry's invocation—not the runtime's structural fallback—owns body
3050
3079
  requiredness and roles. The executor independently rejects an unregistered
3051
3080
  target at its run boundary.
3052
3081
 
@@ -3134,12 +3163,12 @@ addressed to `<runtime>:///<id>` when the id holds an execution only under anoth
3134
3163
  §exec-lifetime **How long a spawn may live is the fence's metadata, one field.**
3135
3164
  `[{"lifetime": …}]` takes a duration (`30s`, `30m`, `2h`), or one of three words;
3136
3165
  absent is `loop`. The key is one of the service's reserved metadata keys, withheld
3137
- from every owner by the framework ({§service-metadata-keys}). An execution takes no scope: a numeric coordinate on an
3138
- executor target is refused `scope-unsupported` (400), naming the field.
3166
+ from every owner by the framework ({§service-metadata-keys}). An execution takes no scope: a scope on an
3167
+ execution is refused `scope-unsupported` (400).
3139
3168
 
3140
3169
  | `lifetime` | The spawn |
3141
3170
  | ------------ | --------- |
3142
- | 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. `` Recovery: `` Run it again with a longer lifetime, such as [{"lifetime": "30m"}], or with none to let it run as long as the loop. `` |
3171
+ | 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. `` |
3143
3172
  | `loop` (absent) | Loop-life bounded: reaped on every loop terminal except 202, the background-stream behavior. |
3144
3173
  | `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}). |
3145
3174
  | `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. |
@@ -3151,7 +3180,7 @@ stream deltas. Calendar recurrence remains a schedule ({§schedule-delivery}).
3151
3180
 
3152
3181
  §exec-host-proposes **Effect-gating.** Each executor — and each scheme operation that mutates something outside this process — declares an `effect` (`pure` | `read` | `host`); the service maps it to policy (`EffectPolicy`). The declarer states the FACT, the panel decides the POLICY, and one rule covers every operation: nothing that changes the world runs on nobody's authority. A `host` runtime (subprocess; file-backed sqlite) proposes under {§proposal}, and so does an outbound request that mutates a remote resource ({§http-outbound-proposes}). Once accepted, it spawns and writes channels at its workspace execution address ({§execution-output-identity}), returning `102 Processing`. Channel state transitions (`active` → `closed`/`errored`) drive subsequent observations ({§channel-state}).
3153
3182
 
3154
- §entry-owner **Every entry belongs directly to one workspace.** Its immutable identity is `(workspace_id, scheme, authority, pathname)`. The workspace foreign key supplies lifetime; URI authority supplies the literal resource namespace. There is no entry-owner Worker, synthetic commons actor, caller-relative alias, or per-Worker access grant. Core binds one canonical coordinate through {§entry-address-resolution} for every operation and consumer. Producer and subscriber Worker ids describe causal activity, not ownership.
3183
+ §entry-owner **Every entry belongs directly to one workspace.** Its immutable identity is `(workspace_id, scheme, authority, pathname)`. The workspace foreign key supplies lifetime; URI authority supplies the literal resource namespace. Core binds one canonical coordinate through {§entry-address-resolution} for every operation and consumer. Producer and subscriber Worker ids describe causal activity, not ownership.
3155
3184
 
3156
3185
  §execution-output-identity **Execution output belongs to the workspace.**
3157
3186
  Each invocation claims a collision-checked eight-character lowercase hexadecimal
@@ -3345,7 +3374,7 @@ body prefixes.
3345
3374
  wholesale ({§log-sensitive-request-evidence}); the spawn's record names each such value's
3346
3375
  provenance as the modifier's.
3347
3376
  - §exec-hold-until-concluded **The turn-hold exception** — for runtimes in `PLURNK_SERVICE_EXEC_HOLD` (a decision-table env), an in-flight stream **pauses the cycle**: the next packet does not assemble until the stream concludes, so the model never burns a turn asking "are we there yet" about a result the engine controls end-to-end. This exception is limited to seconds-bounded runtimes whose final result the engine controls end-to-end. Bounded by `PLURNK_SERVICE_EXEC_HOLD_MS` and **fail-open**: at the cap the standard cycle resumes untouched (waits, wakes, polls). Zero grammar or teaching surface — the model emits an executor fence, optionally followed by WAIT; the wake-shaped world simply arrives one packet sooner. It extends selected runtimes beyond the ordinary {§worker-optimistic-settlement} cap before the next packet assembles. A bare entry holds ALL of a runtime's spawns; a `<runtime>:<effect>` suffix (`github:read`) holds only that effect-class — an MCP server is one runtime whose tools split (a `read` `get_issue` is instant; a `host` `run_migration` is a slow mutation), so an operator opts the known-fast read-class in without parking on the mutation. Conservative stays default: an arbitrary third-party server's latency never parks the engine unless a suffix opts a class in.
3348
- - §exec-entry-sink **The entry() sink** implements {§executor-entry-sink} over ordinary scheme-owned entries. Core owns allocation, materialization, and persistence; executors receive only the returned resource address. Web acquisition and materialization are the `https` handler's {§web-materialization-contract}, reached through the scheme registry; core names no leaf package.
3377
+ - §exec-entry-sink **The entry() sink** implements {§executor-entry-sink} over ordinary scheme-owned entries. Core owns allocation, materialization, and persistence; executors receive only the returned resource address. Web acquisition and materialization are the `https` handler's {§web-materialization-contract}, reached through the scheme registry; core names no extension package.
3349
3378
 
3350
3379
  | Input / effect | Consumer behavior |
3351
3380
  | --- | --- |
@@ -3353,7 +3382,7 @@ body prefixes.
3353
3382
  | Null path + supplied content | Publish beneath the invocation's `resources/` using {§resource-publication-names}, owned by the calling Worker. |
3354
3383
  | Supplied bytes | Retain the original bytes and declared mimetype in an ordinary channel ({§binary-parity}). |
3355
3384
  | Supplied text | Preserve text resources; HTTP/HTML materialization retains source and derived channels under {§html-materialization}. |
3356
- | Null content | Acquire an HTTP(S) resource through the checked WebFetcher, using the same configured materializers as exact HTTP acquisition ({§http-materializer-plugins}). |
3385
+ | Null content | Acquire an HTTP(S) resource through the checked WebFetcher, using the same configured materializers as exact HTTP acquisition ({§http-materializer-extensions}). |
3357
3386
  | Durable evidence | One typed EDIT event in the runtime actor, with the calling Worker as causal source. Binary evidence describes the resource; its complete bytes live in the resource channel. |
3358
3387
  | Model orientation | The executor includes returned addresses in its result. Publication does not independently wake inference or broadcast an observer row ({§env-delta-entry-materialization}). READ controls content acquisition and native delivery. |
3359
3388
 
@@ -3393,10 +3422,10 @@ Core derives the contracts-owned `ProposalProjection` from the durable proposed
3393
3422
  | `target` | canonical `{ scheme, authority, pathname }` from `attrs.proposalTarget` for staged COPY/MOVE, otherwise the log row target |
3394
3423
  | `body` | proposed operation result `rx.body`; absent means the empty review body |
3395
3424
  | `attrs` | proposed log row `attrs` object |
3396
- | `policy` | validated complete persisted loop policy |
3425
+ | `owner` | the worker's durable workspace-scoped approval owner ({§worker-ownership}) |
3397
3426
  | `disposition` | {§proposal-disposition}; the same value drives automatic settlement and client presentation |
3398
3427
 
3399
- Workspace scope remains the event envelope / seam argument ({§notifications-envelope-carries-workspaceid}); it is not forged into `ProposalProjection`. Malformed durable JSON, target metadata, result envelopes, loop policy, or final projection fails at core with its cause; after insertion, core terminalizes that row as a 500 `policy_failed` before propagating the internal failure, so no waiter or durable stopped world is orphaned.
3428
+ Workspace scope remains the event envelope / seam argument ({§notifications-envelope-carries-workspaceid}); it is not forged into `ProposalProjection`. Malformed durable JSON, target metadata, result envelopes, owner registration, or final projection fails at core with its cause; after insertion, core terminalizes that row as a 500 `policy_failed` before propagating the internal failure, so no waiter or durable stopped world is orphaned.
3400
3429
 
3401
3430
  ### §client-interactions Client-owned interaction lifecycle
3402
3431
 
@@ -3405,7 +3434,8 @@ A scheme or executor may pause its current operation on one
3405
3434
  pending-only `client_interactions` row bound to one exact
3406
3435
  workspace/worker/loop/turn ownership chain, then publishes the same validated
3407
3436
  `ClientInteractionProjection` through `loop/interaction` and reconnect
3408
- discovery. The projection contains no workspace id or private upstream
3437
+ discovery. The projection names its recipient ({§client-interaction-routing}) but
3438
+ contains no workspace id or private upstream
3409
3439
  continuation state; those remain respectively in the event envelope and the
3410
3440
  awaiting operation owner.
3411
3441
 
@@ -3423,112 +3453,44 @@ composes it with the existing owner signal before registering the same waiter.
3423
3453
  Reconnect discovery intersects durable rows with live waiters; restart
3424
3454
  removes ownerless rows without fabricating cancellation, payload, or replay.
3425
3455
 
3426
- ### Loop disposition and client YOLO
3456
+ ### Server disposition and client YOLO
3427
3457
 
3428
- Side-effecting operations propose ({§exec}) and pause dispatch at 202 for an
3429
- authority decision ({§engine-rails}, {§methods}). Automatic acceptance has two
3430
- distinct owners:
3431
-
3432
- | Mechanism | Authority path | Intended use |
3433
- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
3434
- | §proposal-ownership-loop-auto **Loop disposition** | `runLoop({ policy: { proposals: "accept" } })` persists a loop-owned disposition; core resolves proposals in process without a client. | Headless automation, benchmarks, CI, fixtures, and unattended use. |
3435
- | **Client-side YOLO** (`--yolo` / `PLURNK_YOLO`) | A `proposals: "review"` loop emits the ordinary `loop/proposal`; the client returns an accepted proposal through its standard resolution path. | Interactive automatic review. |
3436
-
3437
- Core cannot distinguish client-side YOLO from a fast human acceptance and does
3438
- not need to. Loop auto keeps authority inside the loop; client-side YOLO acts
3439
- only after authority crosses the client boundary.
3458
+ | Mechanism | Authority path |
3459
+ | --- | --- |
3460
+ | Server disposition | `PLURNK_SERVICE_PROPOSALS` selects review, accept, or reject. Core settles automatic decisions through the normal proposal lifecycle. |
3461
+ | Client-side YOLO | The owning client accepts review requests through the same resolution path as human review. It cannot override server rejection or capability admission. |
3440
3462
 
3441
- §proposal-ownership-notification **The notification carries disposition, not policy inputs.** `loop/proposal` carries the core-owned `ProposalDisposition` ({§notifications}, {§proposal-disposition}). A connected client presents only `owner="client"`; it never reimplements policy from operation or attrs.
3463
+ §proposal-ownership-notification The notification carries the selected
3464
+ `ProposalDisposition`, not policy inputs. Clients present only `decision: "review"`
3465
+ addressed to their owner identity; they never reconstruct authority from the
3466
+ operation, message sender, or attrs.
3442
3467
 
3443
3468
  ### §proposal-disposition Settlement authority and precedence
3444
3469
 
3445
- §loop-attendance **A loop says whether anyone is attending, and a wait nobody could end is
3446
- never taken.** `LoopPolicy.attended` is the whole of it: `true` says an interactive partner is
3447
- present, `false` declares an unattended loop. It is one field of the loop's complete policy, so
3448
- its creator states it or leaves it to the panel ({§loop-policy-composition}); the client's
3449
- `--auto` is exactly the statement `attended: false`. A fresh delegated loop inherits it with the
3450
- rest of the policy ({§worker-delegation-inherits-policy}), so a child of a headless run is
3451
- headless too.
3452
-
3453
- Unattended, three things follow and nothing else does:
3454
-
3455
- - **A proposal is never held for review.** `{ proposals: "review", attended: false }` is not a
3456
- `LoopPolicy`: the schema refuses the pair, so no loop can persist it. The panel cannot produce
3457
- it, because attendance picks which disposition knob answers. Only a creator's own statement
3458
- can ask for it, and that is refused **400 `loop-policy-invalid`** naming the way out.
3459
- - **No interactive partner is offered.** `ClientInteractions.request` refuses **501
3460
- `loop-unattended`** instead of writing the request down and waiting. Every wiring funnels
3461
- through that one request — the `question` runtime ({§question-tool}), the execution input
3462
- bridge, the scheme interaction caps, and MCP elicitation — so one refusal covers them all. The
3463
- `question` runtime's effect is `read`, so it is never proposal-gated and a disposition does
3464
- nothing for it. The refusal is the asking executor's **own result**, never a thrown contract
3465
- violation: the model has to read why it cannot ask.
3466
-
3467
- Dispatch refuses it first, at the **loop ring** of the capability cascade: an unattended loop's
3468
- own layer denies the `interact` access class ({§worker-tool-admission}), and that 403 names the
3469
- ring, the reason and the recovery — a subtracted tool must say why it is gone and what to do
3470
- instead. Every other ring is operator configuration and speaks for itself; the loop ring is the
3471
- one the model can act on. The 501 at the interaction itself is the backstop for the paths that
3472
- do not cross dispatch (MCP elicitation raised inside a tool call, the execution-input bridge).
3473
- The ring reaches dispatch but not the reserved tree's listing, which is one artifact per
3474
- workspace: an unattended model still sees the question document and learns at dispatch that it
3475
- is refused (#770).
3476
- - **A provider-recovery park becomes a conclusion** ({§provider-recovery}), carrying the
3477
- provider's own exact Problem. Never a substituted "the model gave up".
3478
-
3479
- **A park is a promise that something will restart it.** `LoopLifecycle.park` takes the waker
3480
- by name; `null` says nothing will. Parking an unattended loop with no waker is a contract
3481
- violation and throws, so a park site added later fails loudly on its first unattended run
3482
- instead of idling until some caller's clock notices. It is a tripwire, not a fallback: the
3483
- provider-recovery path concludes before reaching it.
3484
-
3485
- Attendance never converts a legitimate wait that has a real waker — a `WAIT`, an open stream,
3486
- a delegated child — into a termination. Those have wakers; a human question
3487
- does not. A prompt prefix states a disposition, never an attendance: typing `?` asks for review,
3488
- and an unattended loop refuses that statement rather than conjuring a reviewer.
3489
-
3490
- §loop-policy-composition **A loop's policy is what its creator stated over what the panel
3491
- says.** A creator — a client's `loop.run`, a schedule definition, a transport module — states
3492
- any part of a policy as a `LoopPolicyRequest`, or nothing. The request stays exactly as stated
3493
- until a fresh loop is persisted: an omitted field is no opinion, so a fold compares only what
3494
- was said ({§methods-loop-run-fold-consistency}). `LoopPolicies.compose` then makes it whole,
3495
- once. `PLURNK_SERVICE_ATTENDED` answers an unstated attendance, and the attendance picks which
3496
- knob answers an unstated disposition: `PLURNK_SERVICE_PROPOSALS` for an attended loop,
3497
- `PLURNK_SERVICE_UNATTENDED_PROPOSALS` for an unattended one, whose vocabulary has no `review`.
3498
- Every valid panel state is therefore lawful; an invalid knob is diagnosed at startup
3499
- and refuses a loop that needs it ({§configuration-repair-path}). No code,
3500
- schema or column holds a default ({§operator-config-only-home}): `loops.policy` and
3501
- `loops.max_turns` carry none, so every insert states both. Client-authored administrative
3502
- loops use the same composition.
3503
-
3504
- §runtime-bookkeeping-policy **Runtime bookkeeping has no reviewer and cannot acquire
3505
- new authority.** Its administrative loops explicitly state
3506
- `{ attended: false, proposals: "reject" }`; this is a runtime invariant, not an
3507
- interactive default. Generated reference publication and audit narration therefore
3508
- do not depend on client policy configuration. Runtime-authored proposals do not
3509
- use effect-policy auto-admission; bookkeeping proposals settle as failures through
3510
- the ordinary proposal lifecycle, never wait for a client or auto-accept.
3511
-
3512
- §loop-policy-effective-read `loops.policy` persists one complete immutable
3513
- `LoopPolicy`; every runtime policy read validates that snapshot before use.
3514
- Missing rows or invalid values fail with the owning loop coordinate and cause.
3515
- Raw archival copies and forensic rendering do not interpret policy.
3516
-
3517
- The cascade's rings are `service`, `workspace` and, innermost, `loop`. The loop ring exists only
3518
- for an unattended run and is purely subtractive like every other layer, so it can never widen what
3519
- its workspace allows; a capability denial names the ring that refused. The operator's capability
3520
- projection and the shared reserved-document materialization take no loop coordinate on purpose:
3521
- those questions are about a workspace, not about one run.
3522
-
3523
- `ProposalDisposition` is either `{ owner: "client" }` or `{ owner: "loop", decision: "accept" | "reject", outcome? }`. The persisted loop policy determines it exactly:
3524
-
3525
- | `policy.proposals` | Disposition |
3526
- | ------------------ | ---------------------------------------- |
3527
- | `review` | client |
3528
- | `accept` | loop accept |
3529
- | `reject` | loop reject, outcome `no_review_channel` |
3530
-
3531
- Capability admission precedes this decision, so proposal disposition cannot grant a denied capability. Loop-owned settlement occurs before observational notification; observer failures are diagnosed with their cause and cannot change disposition or leave an eligible automatic proposal pending.
3470
+ | First applicable condition | Disposition |
3471
+ | --- | --- |
3472
+ | Runtime bookkeeping | reject, `runtime_bookkeeping` |
3473
+ | Server `accept` | accept |
3474
+ | Server `reject` | reject, `policy_veto` |
3475
+ | Server `review`, owner implements `request_approval` | review |
3476
+ | Server `review`, owner has no reviewer | reject, `no_review_channel` |
3477
+
3478
+ Core selects the disposition once for the pending gate. Its live event and
3479
+ reconnect projection use that same decision. Automatic settlement precedes
3480
+ observational notification; an observer failure cannot change authority or orphan
3481
+ a waiter. Invalid configuration is diagnosed at the proposal boundary without
3482
+ substituting authority ({§configuration-repair-path}). Client-authored
3483
+ administrative operations retain their client's owner.
3484
+
3485
+ §runtime-bookkeeping-policy Runtime bookkeeping cannot acquire new authority.
3486
+ Generated reference publication and audit narration have no reviewer and do not
3487
+ depend on interactive defaults. An unexpected bookkeeping proposal rejects through
3488
+ the ordinary lifecycle, even if the server would accept other operations.
3489
+
3490
+ A park requires a live obligation or an interactive owner. Connection presence
3491
+ does not determine capability. Ordinary WAIT on streams or delegated work is
3492
+ unchanged; a provider-recovery park whose owner is not interactive concludes on the
3493
+ provider failure ({§provider-recovery}).
3532
3494
 
3533
3495
  ---
3534
3496
 
@@ -3538,7 +3500,7 @@ Capability admission precedes this decision, so proposal disposition cannot gran
3538
3500
 
3539
3501
  ### §subscriptions Subscriptions
3540
3502
 
3541
- §subscriptions-subscription-registry-routes-cancellation READ on a streaming scheme is a subscription, not a one-shot. The scheme establishes its protocol-specific acquisition boundary, returns `102 Processing`, and stays alive through the `StreamSubscription` returned by `subscriptions.open()`. The service commits that initial operation result normally; later chunk and terminal work cannot rewrite it. Durable terminal truth lives on the subscription and its channels. The service records durable subscription identity and metadata in SQLite and retains the callable `SubscriptionHandle` only in its process-local live registry. Worker cancellation, turn-scoped reap, and shutdown all route through that one live registry; no handler-specific cancellation hook or database access is part of the plugin contract.
3503
+ §subscriptions-subscription-registry-routes-cancellation READ on a streaming scheme is a subscription, not a one-shot. The scheme establishes its protocol-specific acquisition boundary, returns `102 Processing`, and stays alive through the `StreamSubscription` returned by `subscriptions.open()`. The service commits that initial operation result normally; later chunk and terminal work cannot rewrite it. Durable terminal truth lives on the subscription and its channels. The service records durable subscription identity and metadata in SQLite and retains the callable `SubscriptionHandle` only in its process-local live registry. Worker cancellation, turn-scoped reap, and shutdown all route through that one live registry; no handler-specific cancellation callback or database access is part of the extension contract.
3542
3504
 
3543
3505
  The durable row is lifecycle evidence and the lookup key, not a serialized callback. `subscriptions.open()` establishes both halves before yielding a composed `StreamSubscription`: an `AbortSignal` whose fused `notifyChunk` and terminal `close` methods are safe to retain without the operation's general `SchemeCtx`. `close(result, summary?, channelResults?)` validates one universal terminal producer result plus exact named channel overrides. One SQLite transition closes the subscription and installs each channel's terminal `producerResult`; its lifecycle state derives from that result. The transition then wakes the worker when appropriate and unregisters the live handle. `close_status` is a constrained relational projection of `close_result.status`, never an independent result, while `channel_results` preserves historical overrides after a later subscription replaces the channel's current evidence. A durable open row without a live handle is an explicit lifecycle failure, never a fabricated cancellation success. Channel state ({§channel-state}) + log entries ({§no-chunk-rows}) carry lifecycle.
3544
3506
 
@@ -3559,7 +3521,7 @@ the producer's already obtained outcome into a fictitious cancellation result.
3559
3521
  At process restart every still-open row is necessarily missing its callable owner. Boot
3560
3522
  settles it as interruption (`500`) and errors active channels before evaluating parked
3561
3523
  loops ({§worker-lifecycle-restart-recovery}); it never reports cancellation (`499`) or
3562
- pretends to reconstruct an opaque plugin connection.
3524
+ pretends to reconstruct an opaque extension connection.
3563
3525
 
3564
3526
  §subscriptions-causal-resource A subscription's causal Worker belongs to the
3565
3527
  entry's workspace. The active entry has
@@ -3615,12 +3577,12 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
3615
3577
  | Concern | Rule |
3616
3578
  |---|---|
3617
3579
  | §db-schema-baseline Baseline | Versions 1–8 of `migrations/` are the released baseline, as domain chapters — `001_workspaces`, `002_workers`, `003_loops`, `004_inference`, `005_entries`, `006_log`, `007_subscriptions`, `008_interactions` — each one `MIGRATE` block whose version is the file's numeric prefix. They create the shape 1.21.1 shipped: tables, indexes, views, the constraint triggers that are a table's invariants (guards that only `RAISE`), and a view's `INSTEAD OF` write path. No migration holds an `INIT` block or a trigger that writes a row. |
3618
- | §db-migrations Evolution | A released version is frozen: only its comments may change. Every shape change is one new file at the next version (`009_effort` renames the #877 columns), applied by sqlrite above the database's `PRAGMA user_version`, ascending, each in its own transaction with its version bump. A fresh database takes the same path as an existing one. Each migration carries upgrade coverage: `test/intg/schema-baseline.test.ts` pins the released shape's fingerprint and migrates a released database, asserting its rows survive. A process trigger's change needs no migration: its `INIT` block re-declares it on the next open ({§db-process-triggers}). A table anything references cannot be rebuilt in a migration: foreign keys stay enforced inside the migration transaction, so the drop cascades through its children (`011_settled.sql`); such a table evolves by adding columns or redeclaring its guard triggers. Only an unreferenced table is rebuilt (`010_outside_text.sql`). |
3580
+ | §db-migrations Evolution | A released version is frozen: only its comments may change. Every shape change is one new file at the next version, applied by sqlrite above the database's `PRAGMA user_version`, ascending, each in its own transaction with its version bump. A fresh database takes the same path as an existing one. Each migration carries upgrade coverage: `test/intg/schema-baseline.test.ts` pins the released shape's fingerprint and migrates a released database, asserting its rows survive. A process trigger's change needs no migration: its `INIT` block re-declares it on the next open ({§db-process-triggers}). A table anything references cannot be rebuilt in a migration: foreign keys stay enforced inside the migration transaction, so the drop cascades through its children (`011_settled.sql`); such a table evolves by adding columns or redeclaring its guard triggers. Only an unreferenced table is rebuilt (`010_outside_text.sql`). |
3619
3581
  | §validation-topology Where an invariant is enforced | The SQL core owns each invariant: a chapter's CHECK constraints and guard triggers are its one statement, and a rule two tables share is the same expression over each column (`entry_channel_producer_result_contract` and `subscriptions_result_contract_update` hold the settled-result rule as one text, `011_settled`). Contracts (JSON Schema) are enforced at the gates: a scheme's result entering core (`Results` in plurnk-schemes) and the wire leaving to clients (plurnk-agui's `Validator` calls). Everything between trusts core and the gates and carries no defensive re-validation: a result read back from a row is parsed, never re-asserted. Witness: `test/intg/validation-topology.test.ts` applies one corpus of settled results to the gate, to chapter 5 and to chapter 7 and asserts the three agree on every row. |
3620
3582
  | Open failure | A missing table or column after migration means the file's shape disagrees with its version: a database from a newer release, or one from an unreleased development build. The daemon refuses to open it and names both remedies. |
3621
3583
  | §db-process-triggers Processes beside their owners | A trigger that writes rows — a cascade, a capture, an ambient event, a publication cursor, a landed curation — is a process, not shape. It is declared as an `-- INIT: <trigger name>` block in the `.sql` file beside the statements that fire it (`ambient.sql` for the ambient feed, `LoopLifecycle.sql`, `Turn.sql`, `Engine.sql` for model calls, `_entry-crud.sql`, `Log.sql`, `ChannelWrite.sql`), as `DROP TRIGGER IF EXISTS` then `CREATE TRIGGER`, so the definition is current on every open of a database whose shape is current. `MIGRATE` always precedes `INIT` and `INIT` runs on the writer only, so a process may reference any table regardless of file order and never runs on the read pool. `test/intg/schema-composition.test.ts` fails on a baseline trigger that writes, an `INIT` trigger that only guards, a block not named after its trigger or not dropping first, and a live trigger set that differs from the declared set after a first and a second open. |
3622
3584
  | §db-fk-indexes Foreign-key check paths | Every foreign-key column a delete, cascade, or parent replacement can check carries an index (partial where the column is nullable), and no registry statement's plan scans a growing table: `test/intg/schema-query-plans.test.ts` runs `EXPLAIN QUERY PLAN` over every `-- PREP` statement against the baseline and fails on a `SCAN` of a growing table, except statements that read a whole table by design (digest, startup recovery, whole-workspace listings, scheduled-loop claims). An index claim is a plan, never a grep of index names. |
3623
- | §db-index-owners Every index has an owner | An explicit index earns its place one of three ways: a registry statement's plan uses it, its leading column is a foreign key whose check it serves, or it enforces uniqueness. The same test fails on any other index, naming it: an index nobody reads is a write on every insert. Duplicates of a `UNIQUE` constraint's own index and sort-only indexes no plan selects were removed on this rule; a column no statement reads (`symbol_refs.col`, `ambient_events.created_at`) is not stored. |
3585
+ | §db-index-owners Every index has an owner | An explicit index earns its place one of three ways: a registry statement's plan uses it, its leading column is a foreign key whose check it serves, or it enforces uniqueness. The same test fails on any other index, naming it: an index nobody reads is a write on every insert. A column no statement reads is not stored. |
3624
3586
  | §db-maintenance-optimize Statistics at shutdown | The daemon's last database step before the caller closes SQLite is `PRAGMA optimize` on the writer (`maintenance_optimize`), so `sqlite_stat1` reflects tables the connection planned against, bounded by SQLite's own analysis limit; a failure there is a reported shutdown error, never silent. Retention runs before it under the operator's policy ({§retention-policy}) and ends with a WAL truncation ({§db-space-reclamation}); no periodic `ANALYZE` runs. |
3625
3587
  | §db-space-reclamation The daemon keeps its own file healthy | `PLURNK_SERVICE_AUTO_VACUUM` (`incremental` or `none`) names the mode the daemon keeps its file in. At start, before any drain, a database in another mode is converted (set the mode, one `VACUUM`, which rewrites the file and needs free disk about its size) and the journal says so with page counts before and after. Under `incremental`, every retention pass ends by stepping `PRAGMA incremental_vacuum` to completion once free pages reach `PLURNK_SERVICE_RECLAIM_MIN_FREE_BYTES` (0 = every pass), and reports `reclaimedPages`; below the floor, free pages stay for SQLite to reuse. Under `none` the file never shrinks and freed pages are reused. No operator step is involved beyond the knobs. The WAL stays bounded by SQLite's automatic checkpoint (#764). |
3626
3588
  | §content-store Every body is stored once | `contents` holds each settled body once, addressed by its SHA-256, however many channels, workspaces, forks or derivations carry it; rows are immutable. `entry_channel_rows` points a settled channel at its body and keeps an active stream's body as a private buffer until it settles, when it is interned. Every reader and writer uses the `entry_channels` view, whose `INSTEAD OF` triggers intern bodies, refuse a bound `content_hash` that is not the content's, and write each column group only when it changed, so a search attachment is never a representation write. SQLite counts no changes for a view, so a write that must know whether its channel exists returns the channel's name; an outer join cannot flatten the view, so the two statements that need one read `entry_channel_rows` and `contents` directly. `derivation_fts` is an external-content index over `derivation_texts` (a derivation joined to its body); `derivations.content_id` names the indexed text, and the triggers in `_entry-fts.sql` move the index with it and forget it on delete. A body no channel holds and no derivation indexes is collected by retention under `PLURNK_SERVICE_COLLECT_CONTENTS`. Witnesses: `test/intg/retention.test.ts`, `test/intg/entries.test.ts`, `test/intg/fulltext-index.test.ts`. |
@@ -3645,7 +3607,7 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
3645
3607
  - Tokenization (provider-bound; hot-swap re-tokenizes per {§tokenomics}).
3646
3608
  - Provider dispatch, request-accounting validation, and exact-decimal aggregate projection through the shared contracts-owned path.
3647
3609
  - Scheme-handler invocation (connections, subprocesses, fetch).
3648
- - Plugin loading ({§plugin-discovery}).
3610
+ - Extension loading ({§extension-discovery}).
3649
3611
  - Stream AbortController lifecycle.
3650
3612
  - CLI + daemon.
3651
3613
 
@@ -3653,16 +3615,16 @@ When SQL becomes onerous for a specific case, retreat for that case and document
3653
3615
 
3654
3616
  ---
3655
3617
 
3656
- ## §core-plugin-composition Plugin composition
3618
+ ## §core-extension-composition Extension composition
3657
3619
 
3658
- The metaproject contract owns installed membership, the one-family manifest
3659
- shape, and the shared pre-import trust boundary ({§plugin-discovery}). Each
3660
- capability framework owns its typed discovery result and trusted loading path.
3661
- Core owns only cross-family composition, arbitration, and operator presentation
3620
+ The metaproject contract owns installed membership, the one-kind manifest
3621
+ shape, and the shared pre-import trust boundary ({§extension-discovery}). Each
3622
+ framework owns its typed discovery result and trusted loading path.
3623
+ Core owns only cross-kind composition, arbitration, and operator presentation
3662
3624
  of skipped-package evidence.
3663
3625
 
3664
- §plugin-namespace-arbitration **Every addressable scheme name has one claim.**
3665
- For an installed plugin, claim identity is the capability family plus its npm
3626
+ §extension-namespace-arbitration **Every addressable scheme name has one claim.**
3627
+ For an installed extension, claim identity is its kind plus its npm
3666
3628
  package name. Core's bundled names are reserved claims. A daemon module's
3667
3629
  runtime registration names its module owner and makes one composite executor
3668
3630
  claim: its ordinary output scheme and optional resource facet do not compete
@@ -3672,14 +3634,14 @@ claiming either name, for process-wide and workspace-scoped registrations alike.
3672
3634
  | Existing claim | Incoming claim | Outcome |
3673
3635
  |--------------------------------|---------------------------------------------------------|---------|
3674
3636
  | None | Any valid claim | Register it. |
3675
- | Same installed family/package | Rescan of the same name | No-op; retain the one registered handler. |
3676
- | Reserved core name | Any plugin or module | Fail naming the reserved owner and claimant. |
3677
- | Any plugin/module | A different owner, including scheme/executor either way | Fail naming both owners. |
3637
+ | Same installed kind/package | Rescan of the same name | No-op; retain the one registered handler. |
3638
+ | Reserved core name | Any extension, a module included | Fail naming the reserved owner and claimant. |
3639
+ | Any extension | A different owner, including scheme/executor either way | Fail naming both owners. |
3678
3640
  | Module runtime | Its optional same-registration scheme facet | Compose one handler under the runtime's single claim. |
3679
3641
 
3680
3642
  Arbitration precedes host registry mutation. External scheme descriptors are
3681
3643
  arbitrated before core imports their handlers; executor packages have already
3682
- crossed their family-owned trusted loading path when core arbitrates their
3644
+ crossed their framework-owned trusted loading path when core arbitrates their
3683
3645
  output faces. A rejected claim leaves both scheme and executor registries
3684
3646
  unchanged, so registration order cannot turn a collision into precedence.
3685
3647
  Installed third-party packages enter through the same scope-agnostic npm
@@ -3690,15 +3652,21 @@ registration.
3690
3652
 
3691
3653
  ## §bundled-set Bundled Set
3692
3654
 
3693
- Family discovery ({§plugin-discovery}) scans installed scoped and unscoped
3655
+ Extension discovery ({§extension-discovery}) scans installed scoped and unscoped
3694
3656
  packages carrying the applicable `plurnk.kind` declaration.
3695
3657
 
3696
- §default-plugin-ownership `@plurnk/plurnk-service` is the sole manifest owner
3697
- of the default leaf set. Capability frameworks own contracts, discovery, and
3698
- loading; their runtime dependency graphs contain no leaf consumers. A required
3699
- default leaf missing from a service install is a broken install. A direct
3700
- framework consumer may intentionally omit leaves and receives that framework's
3701
- documented unavailable-capability behavior.
3658
+ §default-extension-ownership `@plurnk/plurnk-service` is the sole manifest owner
3659
+ of the default extensions. Frameworks own contracts, discovery, and loading;
3660
+ their runtime dependency graphs contain no extension of their kind. A required
3661
+ default extension missing from a service install is a broken install. A direct
3662
+ framework consumer may intentionally omit extensions and receives that framework's
3663
+ documented behavior for an absent extension.
3664
+
3665
+ `@plurnk/plurnk-tavily-plugin` is the separately installed, opt-in plugin showcase,
3666
+ not a service dependency. Its own tests own installation, native discovery,
3667
+ configuration, and HTTP materialization; service installation coverage proves it
3668
+ is absent from the default composition.
3669
+
3702
3670
  Packed executor coverage distinguishes installation from enablement: verify
3703
3671
  the package floor, explicit opt-in, and composed allowlist/disable behavior
3704
3672
  through discovery and registration outside the development dependency graph
@@ -3708,32 +3676,31 @@ service runtime dependency, and an optional one ({§mimetype-optional-grammars})
3708
3676
  must not be. Installation coverage loads each default grammar and verifies
3709
3677
  source definitions, references, structural projections, and teardown outside
3710
3678
  the checkout's development dependency graph, and proves each optional language
3711
- degrades by name when its leaf is absent.
3679
+ degrades by name when its grammar package is absent.
3712
3680
 
3713
3681
  §install-root-advisory-ownership **The composed service install owns
3714
- third-party advisory detection.** Only that install resolves the default leaves
3682
+ third-party advisory detection.** Only that install resolves the default extensions
3715
3683
  and their combined transitive tree. Its audit reports advisories at the
3716
3684
  moderate floor without blocking by default; strict mode makes the same floor a
3717
3685
  gate. A chain rooted through an `@plurnk/*` dependency routes to that package's
3718
3686
  owner, while other direct roots remain service-owned. First-party package-pin
3719
- freshness remains the owning family's concern.
3687
+ freshness remains the owning package's concern.
3720
3688
 
3721
- | Family | Lean framework | Service-owned default leaves |
3689
+ | Kind | Lean framework | Service-owned default packages |
3722
3690
  |-----------|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
3723
3691
  | Schemes | `@plurnk/plurnk-schemes` | `@plurnk/plurnk-schemes-http` |
3724
- | Mimetypes | `@plurnk/plurnk-mimetypes` | `application-ipynb`, `application-json`, `application-jsonl`, and `application-xml` format leaves. |
3725
- | | | `text-csv`, `text-diff`, `text-dotenv`, `text-html`, `text-ini`, `text-markdown`, and `text-plain` format leaves. |
3726
- | | | `image`, `application-pdf` (header-only, {§mimetype-pdf-facts}), and every `grammar-{slug}` leaf in the framework's tree-sitter registry ({§mimetype-grammar-leaves}), all under `@plurnk/plurnk-mimetypes-*`. |
3727
- | Executors | `@plurnk/plurnk-execs` | `common`, `jq`, and `sqlite` leaves under the `@plurnk/plurnk-execs-*` prefix. |
3692
+ | Mimetypes | `@plurnk/plurnk-mimetypes` | `application-ipynb`, `application-json`, `application-jsonl`, and `application-xml` format extensions. |
3693
+ | | | `text-csv`, `text-diff`, `text-dotenv`, `text-html`, `text-ini`, `text-markdown`, and `text-plain` format extensions. |
3694
+ | | | The `image` and `application-pdf` (header-only, {§mimetype-pdf-facts}) extensions, and every `grammar-{slug}` grammar package in the framework's tree-sitter registry ({§mimetype-grammar-packages}), all under `@plurnk/plurnk-mimetypes-*`. |
3695
+ | Executors | `@plurnk/plurnk-execs` | `common`, `jq`, and `sqlite` extensions under the `@plurnk/plurnk-execs-*` prefix. |
3728
3696
 
3729
- The independently published `tokenizers` artifact is an opt-in leaf. Installing
3697
+ The independently published `tokenizers` artifact is an opt-in package. Installing
3730
3698
  it beside the service admits it through ordinary package resolution without
3731
- changing the service manifest. PDF extraction is not a leaf at all: a PDF is a
3732
- native attachment like an image, and the daemon does no extraction (#542).
3699
+ changing the service manifest.
3733
3700
 
3734
3701
  **Providers:** `@plurnk/plurnk-providers` resolves the Models.dev catalog,
3735
3702
  operator declarations, local adapters, and finally installed AI SDK provider
3736
- plugins. `Mock` is its integration fixture. Core contains no vendor protocol.
3703
+ extensions. `Mock` is its integration fixture. Core contains no vendor protocol.
3737
3704
 
3738
3705
  **Core schemes:** `file`, `log`, `prompt`, `skill`, and `worker` expose daemon
3739
3706
  state or filesystem orchestration owned by core. `exec` is internal dispatch
@@ -3744,8 +3711,8 @@ dispatcher contract.
3744
3711
 
3745
3712
  The executor registry discovers installed runtimes, probes availability, and
3746
3713
  routes ```` ```<runtime> ````; core contributes orchestration and the output-scheme
3747
- adapter, not runtime implementations. Optional and third-party leaves extend
3748
- each family by installation and discovery; they never require a framework or
3714
+ adapter, not runtime implementations. Optional and third-party extensions extend
3715
+ each kind by installation and discovery; they never require a framework or
3749
3716
  service manifest edit.
3750
3717
 
3751
3718
  ---
@@ -3813,12 +3780,12 @@ Node's pre-script env-file form and the executable's post-script form share the
3813
3780
 
3814
3781
  | Source | Panel | Admission |
3815
3782
  |---|---|---|
3816
- | Platform capability package | `.env.defaults` at the package root | `@plurnk/*` or a `plurnk` package field; {§plugin-trust-boundary} |
3817
- | Agent Plugin native extension | `ai.plurnk/.env.defaults` | The winning daemon-wide plugin in {§agent-plugins-hosting}, a valid native declaration, and the same trust gate |
3783
+ | Extension package | `.env.defaults` at the package root | `@plurnk/*` or a `plurnk` package field; {§extension-trust-boundary} |
3784
+ | Agent Plugin extension | `ai.plurnk/.env.defaults` | The winning daemon-wide plugin in {§agent-plugins-hosting}, a valid native declaration, and the same trust gate |
3818
3785
 
3819
3786
  Root and trust flags apply before collection. A project plugin contributes no native panel.
3820
- Non-module native panels follow their npm-only family discovery ({§plugin-manifest-read});
3821
- a plain-folder declaration does not suppress an installed capability's panel.
3787
+ Non-module native panels follow their framework's npm-only discovery ({§extension-manifest-read});
3788
+ a plain-folder declaration does not suppress an installed extension's panel.
3822
3789
  Linked packages resolve panels against the same canonical root as native code;
3823
3790
  an absent panel is optional, but a panel escaping that root is rejected.
3824
3791
  The file travels with its code and is its configuration reference. All admitted files compose
@@ -3828,6 +3795,24 @@ persisting another copy or exposing effective values. Duplicate key ownership fa
3828
3795
  owners. Invalid optional native panels are diagnosed and prevent that extension from loading;
3829
3796
  they do not block the remaining floor or the repair path ({§configuration-repair-path}).
3830
3797
 
3798
+ §operator-config-undeclared-key **A key no panel declares is reported, never refused.** At boot the
3799
+ daemon checks each `PLURNK_*` key the operator's configuration files set — `$XDG_CONFIG_HOME/plurnk/.env`
3800
+ and a `--config` file, never an `--env-file*` layer or the process environment — against the
3801
+ assembled panels' declarations, live and commented alike. A declaration whose name holds a
3802
+ `<placeholder>` segment declares a family and is only ever commented. A placeholder, like the scope
3803
+ after a declared name's `_`, stands for one or more letters, digits, `_`, `.` or `-`.
3804
+
3805
+ | Key the file sets | Outcome |
3806
+ |---|---|
3807
+ | A declared name | none |
3808
+ | A declared name, `_`, and a scope (`PLURNK_PROVIDERS_EFFORT_<alias>`, `PLURNK_MODEL_<alias>`) | none |
3809
+ | A family member (`# PLURNK_MCP_<alias>=` declares `PLURNK_MCP_docs_ENABLED`) | none |
3810
+ | Another component's: the client's `PLURNK_CLIENT_*` and `PLURNK_COMPOSITION_*`; the bench's `PLURNK_BENCH*`, `PLURNK_SWEBENCH_*`, `PLURNK_PI_*` and `PLURNK_CANDIDATE_*` | none |
3811
+ | Any other `PLURNK_*` key | one `info` Notice from `engine:configuration`, kind `configuration_undeclared`, naming the `key` and the file |
3812
+
3813
+ Boot continues. The notice is the operator's information: `start`, `config` and `config check` print
3814
+ it, `config check` still passes, and it never enters a model's packet.
3815
+
3831
3816
  §operator-config-only-home **The cascading environment is the only home for a choice.** The principle and its reasons are ARCHITECTURE.md's (*Configuration authority*); this is what `scripts/env-surface-policy.mjs` enforces in `root:lint`, over the source Git tracks:
3832
3817
 
3833
3818
  | Rule | What it refuses |
@@ -3839,7 +3824,6 @@ they do not block the remaining floor or the repair path ({§configuration-repai
3839
3824
  | `tunable` | a numeric constant whose own name says duration, size, count or pacing, unless `scripts/env-surface-mechanism.json` registers why it is mechanism |
3840
3825
  | `timer-literal` | a bare number handed to a timer or a deadline; it is named or read from the panel, and has no register |
3841
3826
  | `dead-knob` | a live declaration nothing consumes |
3842
- | `retired-declared` | a retired key still declared; a retired key is named only by the code that refuses it |
3843
3827
  | `duplicate-owner` | a key two packages declare |
3844
3828
  | `test-floor` | a package that ships a panel and tests off it |
3845
3829
 
@@ -3866,11 +3850,12 @@ names the offending variable or file/entry and retains its cause.
3866
3850
 
3867
3851
  | Owner | Offline validation |
3868
3852
  |---|---|
3869
- | Core | Model selection, file-creation/effect/loop policy, members definitions and controls, skill-fetch settings and root selection |
3853
+ | Core | Model selection, file-creation/effect/approval policy, members definitions and controls, skill-fetch settings and root selection |
3870
3854
  | MCP | Whole definitions from the environment and selected files (cwd is the project for this check), future-alias controls, catalog settings, timeouts, retry pacing and registry URL |
3871
3855
  | A2A | Whole outbound definitions and controls, timeout/diagnostic bounds, configured inbound exposure |
3872
3856
  | Schedule | Whole definitions and controls, recurrence syntax, time zone and preview count |
3873
3857
  | Hooks | Command/argument/event configuration and delivery bounds |
3858
+ | Discovered modules | Each module's own configuration, as its factory constructs it ({§module-self-activation}) |
3874
3859
 
3875
3860
  Disabled definitions and controls without a resource are validated, not skipped.
3876
3861
  Checking creates no database, starts no process or listener, arms no schedule,
@@ -3894,14 +3879,14 @@ Each knob's value lives on its panel and nowhere else (`plurnk-service config de
3894
3879
  |---|---|
3895
3880
  | `PLURNK_SERVICE_DB_PATH` | SQLite file path; an explicit non-empty value overrides the derived default. |
3896
3881
  | `PLURNK_SERVICE_SHARE_FOLDER` | Parent of the shares written when no folder is named ({§share-folder}); empty is `$XDG_STATE_HOME/plurnk/shares`. |
3897
- | §operator-config-shared-keys `PLURNK_HOST`, `PLURNK_PORT` | The listener's bind address and TCP port — THE client surface, the AG-UI+ listener the plurnk-agui module binds at boot; production is single-listener. **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. |
3882
+ | §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. |
3898
3883
  | §operator-config-git-ceiling `PLURNK_SERVICE_GIT_ALLOWED` | Hard service ceiling: only `1` admits Git membership and status; every other value denies them. |
3899
3884
  | §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. |
3900
3885
  | `PLURNK_SERVICE_FILE_MATERIALIZE_MAX_BYTES` | Byte ceiling in `1..104857600` for one workspace-file snapshot ({§membership-materialization-limit}). |
3901
3886
  | `PLURNK_SERVICE_MAX_TURNS` | Operator model-call **ceiling** — `-1` = no cap; a positive value clamps `runLoop({maxTurns})`. The durable worker-tree budget includes descendant calls, BARE, and park/resume under {§turn-cap-counts-the-tree}; non-model chronology consumes none. |
3902
- | `PLURNK_SERVICE_MAX_COMMANDS` | Per-emission action ceiling; `-1` = no cap — every generated op dispatches. A positive value caps dispatched actions: overflow ops drop with one durable `max-commands-exceeded` error row on the next packet. The final disposition always dispatch. Tightened per workspace via `settings.maxCommands` (min wins). |
3887
+ | `PLURNK_SERVICE_MAX_COMMANDS` | Per-emission action ceiling; `-1` = no cap — every compiled op dispatches. Each resolved target statement counts once, whether authored separately or in a group ({§target-group}). Overflow ops drop with one durable `max-commands-exceeded` error row on the next packet. Lifecycle operations remain exempt ({§operator-config-workspace-max-commands-floor}). Tightened per workspace via `settings.maxCommands` (min wins). |
3903
3888
  | §operator-config-loop-timeout `PLURNK_SERVICE_LOOP_TIMEOUT` | Positive ms of cumulative active execution per loop ({§loop-execution-allowance}); excludes parked/queued time. Snapshotted on first execution, retained across wakes. Exhaustion aborts in-flight work and terminates `504 loop_timeout`, including a stuck provider call. |
3904
- | `PLURNK_SERVICE_PROVIDER_RECOVERY` | ms of recovery after the first recoverable provider failure; `0` disables reissue. Expiry parks attended loops, concludes unattended loops, or returns BARE's failure under {§provider-recovery}. |
3889
+ | `PLURNK_SERVICE_PROVIDER_RECOVERY` | ms of recovery after the first recoverable provider failure; `0` disables reissue. Expiry parks workers with a reviewer, otherwise concludes their loop, or returns BARE's failure under {§provider-recovery}. |
3905
3890
  | `PLURNK_SERVICE_PROVIDER_RECOVERY_BACKOFF` | First recovery delay (ms); doubles per failure up to `PLURNK_SERVICE_PROVIDER_RECOVERY_BACKOFF_MAX` ({§provider-recovery}). |
3906
3891
  | `PLURNK_SERVICE_MAX_STRIKES` | Consecutive turn-contract strike threshold ({§engine-rails}). |
3907
3892
  | `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}. |
@@ -3910,8 +3895,6 @@ Each knob's value lives on its panel and nowhere else (`plurnk-service config de
3910
3895
  | `PLURNK_SERVICE_EDIT_RECEIPT_CONTEXT_LINES` | Surrounding and landed lines shown at each EDIT result boundary ({§edit-result-receipt-projection}). |
3911
3896
  | `PLURNK_SERVICE_MIN_CYCLES` | Min repetitions before cycle detection fires ({§engine-rails}). |
3912
3897
  | `PLURNK_SERVICE_MAX_CYCLE_PERIOD` | Max period length cycle detection examines ({§engine-rails}). |
3913
- | `PLURNK_SERVICE_REQUIEM_MAX_TOKENS` | Initial forensic witness output allowance ({§digest-requiem}). |
3914
- | `PLURNK_SERVICE_REQUIEM_RETRY_MAX_TOKENS` | Retry allowance; must be at least the initial requiem allowance ({§digest-requiem}). |
3915
3898
  | `PLURNK_SERVICE_FILES_ITEMS` | Turn-0 catalog preview. Folder-capable schemes render a one-level `*` map with `dir/**` rollups; kernel docs remain recursive and explicitly complete. `-1` = markerless first pages; positive `N` explicitly caps only file-map rows; `0` / unset = off ({§actor-boundary-catalog-preview}). |
3916
3899
  | `PLURNK_SERVICE_MEMBERS_MODEL_SCOPE` | Ceiling for a model's `members` definitions in the lattice `none < root < namespace`; `none` refuses every model definition ({§members-model-scope}). |
3917
3900
  | `PLURNK_SERVICE_EXEC_CONCURRENCY` | Executions admitted at once per workspace; the rest queue FIFO with `202 queued` receipts; `-1` unbounded ({§exec-concurrency}). |
@@ -3935,15 +3918,16 @@ instead of a user's boot, and a dead knob cannot ship.
3935
3918
 
3936
3919
  Feature-flag bools use `process.env.X === "1"` exactly — never `=== "true"`.
3937
3920
 
3938
- External plugins declare their own env vars in their own `.env.defaults`, assembled at boot ({§operator-config-env-defaults}).
3921
+ External extensions declare their own env vars in their own `.env.defaults`, assembled at boot ({§operator-config-env-defaults}).
3939
3922
 
3940
- §operator-config-cli-flags **Admin CLI flags derive only from the service package's `.env.defaults`.** Every `PLURNK_*` declared there becomes `--<kebab-cased-name>` (prefix stripped, lowercased, underscores → dashes). A comment immediately above the declaration becomes its `-h` description. Installed plugin defaults join the environment floor and catalog but do not implicitly expand the service executable's flag surface.
3923
+ §operator-config-cli-flags **Admin CLI flags derive only from the service package's `.env.defaults`.** Every `PLURNK_*` declared there becomes `--<kebab-cased-name>` (prefix stripped, lowercased, underscores → dashes). A comment immediately above the declaration becomes its `-h` description. Installed extension defaults join the environment floor and catalog but do not implicitly expand the service executable's flag surface.
3941
3924
 
3942
3925
  ### Loop limits
3943
3926
 
3944
3927
  §turn-cap-counts-the-tree **The turn ceiling is the worker tree's budget of model
3945
3928
  calls.** The owner is the current loop of the topmost ancestor-or-self worker that has
3946
- one: for a tree a client started, the root worker's loop current when this loop began (its
3929
+ one, excluding the runtime actor (origin `_plurnk`): for a tree a client started,
3930
+ the root worker's loop current when this loop began (its
3947
3931
  `max_turns`: the client's `maxTurns` clamped by the operator ceiling below); a loop with no
3948
3932
  such ancestor owns its own budget. Every model call on the owner's loop and on any later
3949
3933
  loop of the owner's descendants spends it, emission turns and BARE calls alike, open or
@@ -3952,6 +3936,10 @@ terminal ({§loop-terminals}) when the ceiling is met; a BARE beyond the budget
3952
3936
  429 `max-turns` before any provider call, so one turn cannot spend past it with a batch. A
3953
3937
  child loop inherits the value and binds the same count.
3954
3938
 
3939
+ The runtime actor supplies no ceiling and does not combine otherwise independent model
3940
+ subtrees into one allowance. Its descendants still share their own model tree's ceiling.
3941
+ The shipped ceiling is `-1` (uncapped); client and transport defaults introduce no finite cap.
3942
+
3955
3943
  §operator-config-max-turns-ceiling Enforcement is per-use-site — no central most-restrictive pass; each ceiling is checked where it bites. `PLURNK_SERVICE_MAX_TURNS` at `-1` is no cap; when an operator sets a positive value, the per-call request is `min()`-capped against it. Other termination rules remain independent ({§loop-terminals}).
3956
3944
 
3957
3945
  ### Workspace settings
@@ -3984,9 +3972,9 @@ leak into another.
3984
3972
  workspace: a client tightens the runaway-op guard and never raises it past
3985
3973
  the operator's.
3986
3974
  - §operator-config-workspace-max-commands-floor The cap bounds *actions* only.
3987
- The final disposition (`102`, `200`, `202`,
3988
- `300`, or `499`) are never counted and always dispatch, so `0` is a valid
3989
- floor — the tightest — admitting a plan and disposition with zero actions.
3975
+ WAIT and parameterless KILL are never counted and always dispatch, so `0` is a valid
3976
+ floor, admitting lifecycle intent with zero actions. Dropped actions still prevent
3977
+ successful completion under {§turn-disposition}.
3990
3978
  - §operator-config-workspace-git `settings.git` (`false`) **denies** git for the workspace (`PLURNK_SERVICE_GIT_ALLOWED` AND workspace) — the client opts its workspace out of git membership and working-tree status; it can never re-enable git past the operator's service-wide lockout.
3991
3979
  - §operator-config-workspace-file-create-scope `settings.fileCreateScope` narrows `PLURNK_SERVICE_FILE_CREATE_SCOPE` by the ordered lattice `none < root < namespace`; a workspace may disable creation or confine a namespace-enabled service to its root, but never widen the operator's ceiling. Unknown service values fail configuration validation and unknown workspace values fail `workspace.create`.
3992
3980
  - `settings.membersModelScope` narrows `PLURNK_SERVICE_MEMBERS_MODEL_SCOPE` ({§members-model-scope}).
@@ -4004,7 +3992,7 @@ the policy renders in exactly one packet section. Every other tier runs the
4004
3992
  test cascade, so shipped-default regressions are otherwise invisible by
4005
3993
  construction.
4006
3994
 
4007
- §operator-config-real-model-profile **Real-model gate profile.** `plurnk-core/.env.test` is committed source and is the single shared profile for live, demo, and the candidate daemon used by benchlets. Live/demo load it after operator files; the candidate daemon loads it below its inherited environment. Direct shell/benchmark overrides win in both paths. Its exact allowlist is limited to gate-wide service posture that is identical on every machine: complete catalog orientation, automatic Git membership when the operator ceiling permits Git, ambient operator-file docs/packet notes cleared, the operator's installed skills and plugins left unread ({§agent-roots}), ambient MCP/A2A and schedules default disabled, and `PLURNK_EXECS_QUESTION=0` for unattended runs. The ordinary executor switch removes the question tool and its teaching; an explicit override can opt into an attended drill. The drivers also project per-alias `ENABLED=0` overrides for named MCP, A2A, schedule, membership, and skill resources in the operator's config file. Definitions remain inspectable; explicit shell/benchmark controls win. Mock-tier bootstrap clears these ambient families before loading its fixture floor. No operator file is rewritten. Configuration with a narrower or variable owner stays outside it:
3995
+ §operator-config-real-model-profile **Real-model gate profile.** `plurnk-core/.env.test` is committed source and is the single shared profile for live, demo, and the candidate daemon used by benchlets. Live/demo load it after operator files; the candidate daemon loads it below its inherited environment. Direct shell/benchmark overrides win in both paths. Its exact allowlist is limited to gate-wide service posture that is identical on every machine: complete catalog orientation, automatic Git membership when the operator ceiling permits Git, ambient operator-file docs/packet notes cleared, the operator's installed skills and plugins left unread ({§agent-roots}), server approval set to accept ({§worker-owner-resolution}), and ambient MCP/A2A and schedules default disabled. The drivers also project per-alias `ENABLED=0` overrides for named MCP, A2A, schedule, membership, and skill resources in the operator's config file. Definitions remain inspectable; explicit shell/benchmark controls win. Mock-tier bootstrap clears these ambient families before loading its fixture floor. No operator file is rewritten. Configuration with a narrower or variable owner stays outside it:
4008
3996
 
4009
3997
  | Owner | Configuration |
4010
3998
  |---|---|
@@ -4043,9 +4031,10 @@ a fresh-user configuration.
4043
4031
 
4044
4032
  ## §rpc Module seam
4045
4033
 
4046
- Core implements the contracts-owned {§application-port} and owns the typed
4047
- module setup seam. It owns no external listener, public
4048
- action-name catalog, or generic string-dispatched method registry. A
4034
+ Core implements the contracts-owned {§application-port} and hosts the module
4035
+ contract published in `@plurnk/plurnk-modules` ({§module-contract}). It owns the one
4036
+ listener modules mount on ({§http-host}), but no public action-name catalog or generic
4037
+ string-dispatched method registry. A
4049
4038
  client-interface module such as `plurnk-agui` owns its public protocol, action
4050
4039
  names, request validation, discovery result, and event projection.
4051
4040
 
@@ -4053,13 +4042,13 @@ names, request validation, discovery result, and event projection.
4053
4042
 
4054
4043
  ```mermaid
4055
4044
  flowchart LR
4056
- bind["Host may pre-bind client-interface listener<br/>unready"] --> register
4057
- register["Daemon.registerModule"] --> setup["module.setup(ModuleSetupSeam)"]
4045
+ bind["Host binds the daemon's listener<br/>unadmitted"] --> register
4046
+ register["Register explicit, then discovered modules"] --> setup["module.setup(ModuleSetupSeam)"]
4058
4047
  setup --> capabilities["Register static capabilities,<br/>workspace activators, and actions"]
4059
4048
  capabilities --> ready["Process-wide schemes ready"]
4060
4049
  ready --> recovery["Reconcile durable lifecycle"]
4061
4050
  recovery --> start["module.start(ApplicationPort)"]
4062
- start --> interface["Module-owned client protocol<br/>ready"]
4051
+ start --> interface["Listener admitted:<br/>module-owned client protocol ready"]
4063
4052
  recovery -->|durable workspace work| demand["First workspace demand"]
4064
4053
  interface -->|client workspace work| demand
4065
4054
  demand --> lease["Acquire capability residency"]
@@ -4076,17 +4065,17 @@ flowchart LR
4076
4065
  ```
4077
4066
 
4078
4067
  Every registered module's `setup` runs in registration order before any
4079
- module's `start`. Core then readies process-wide schemes, reconciles durable
4080
- lifecycle, and starts modules in registration order. The production
4081
- client-interface module may already own its socket under
4082
- {§startup-listener-admission}; its `start` activates request handling without
4083
- rebinding. Persisted workspaces with
4068
+ module's `start` ({§module-phases}; failures follow {§module-failure}). Core
4069
+ then readies process-wide schemes, reconciles durable lifecycle, and starts
4070
+ modules in registration order. The daemon's listener is bound before durable admission
4071
+ ({§startup-listener-admission}) and admitted once every module has started
4072
+ ({§module-http-mounts}). Persisted workspaces with
4084
4073
  no durable work stay passive until first demand; activation publishes their
4085
4074
  complete capabilities and documentation before the demanding operation
4086
4075
  proceeds. `setup` is the readiness boundary for every capability registered
4087
- with Core: recovery may demand a workspace provider before `start`. For a
4088
- pre-bound client interface, requests remain unavailable until `start`; every
4089
- other module opens its module-owned exterior ingress only after recovery. No
4076
+ with Core: recovery may demand a workspace provider before `start`. Requests on
4077
+ the listener remain unavailable until it is admitted; a module opens any other exterior
4078
+ ingress only after recovery. No
4090
4079
  registered capability may depend on exterior ingress. A module, and any distinct
4091
4080
  lifetime object returned by `start`, may implement the following phases:
4092
4081
 
@@ -4103,21 +4092,26 @@ even if setup fails. A returned object identical to its module is tracked once.
4103
4092
 
4104
4093
  | Source | Declaration | Lifetime |
4105
4094
  |---|---|---|
4106
- | Platform capability package | `package.json#plurnk` with `kind: "module"` and `module` | Daemon-wide |
4095
+ | Extension package | `package.json#plurnk` with `kind: "module"` and `module`, an export subpath resolved through the package's own exports as in {§executor-dynamic-runtimes} | Daemon-wide; the form every first-party default module uses |
4107
4096
  | Agent Plugin | `plugin.json#extensions.ai.plurnk` with `kind: "module"` and a `module` path under `ai.plurnk/` | Daemon-wide; npm and selected user roots only |
4108
4097
  | Project Agent Plugin | Portable components only | Workspace-scoped; native code is not imported |
4109
4098
 
4110
- The export is one DaemonModule object or no-argument factory. Standard bundles follow
4099
+ The export is one DaemonModule ({§module-contract}) object or no-argument factory. The host
4100
+ records each module's owner, the package it came from, and its diagnostics name that owner. The
4101
+ service registers no module itself; a host that registers one explicitly, such as a test
4102
+ substituting a package, names it by its package the same way. Standard bundles follow
4111
4103
  {§agent-plugins-hosting} source order, then other installed module packages load in package-name
4112
- order. All trusted modules register before setup. The service's explicit AG-UI, hooks and MCP
4113
- composition is never duplicated. Untrusted modules are reported and not imported. Invalid
4104
+ order. All trusted modules register before setup; a daemon without a listener then leaves out
4105
+ every one that declares mounts ({§module-http-mounts}). A package the host registered explicitly is
4106
+ never also discovered: discovery skips that owner before importing it. Untrusted modules are reported and not imported. Invalid
4114
4107
  declarations, unavailable module files and configuration errors during construction are diagnosed
4115
- at the affected native extension; healthy siblings remain available. A factory validates startup
4108
+ at the affected extension, a declaration's error owned by `extensions` and a
4109
+ module's own configuration error by `module:<package>`; healthy siblings remain available. A factory validates startup
4116
4110
  configuration before `setup` acquires resources. Failures after registration begins follow
4117
4111
  {§module-lifecycle} cleanup, not a partial-registration fallback.
4118
4112
  An invalid module object, factory result or lifecycle member is an implementation contract failure,
4119
- not configuration, and fails loudly. Native capabilities register through their owning public
4120
- interfaces and release registrations during {§module-lifecycle} resource teardown.
4113
+ not configuration, and fails loudly. What a module contributes registers through its owning public
4114
+ interfaces and is released during {§module-lifecycle} resource teardown.
4121
4115
 
4122
4116
  §module-shutdown-order `Daemon.stop()` first rejects new capability demand and
4123
4117
  aborts proposals, branches, derivations, and worker scopes. It begins module
@@ -4163,18 +4157,21 @@ flowchart LR
4163
4157
  observers --> database[Maintain and release database]
4164
4158
  ```
4165
4159
 
4160
+ §module-host-capabilities **The coordinator's host interface is core's own.**
4161
+ The daemon passes one object implementing every published slice
4162
+ ({§module-seam-slices}); these functions are on it too, for core's
4163
+ Functionality coordinator and core's own witnesses, and are not part of the
4164
+ published module contract.
4165
+
4166
4166
  | Setup function | Contract |
4167
4167
  |---|---|
4168
4168
  | `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. |
4169
- | `registerScheme(name, handler)` | Adds one process-wide addressable scheme handler; scheme readiness and model-facing capability publication remain core-owned. |
4170
- | §module-action-registration `registerModuleAction({ name, scope, residency, inputSchema, outputSchema, handler })` | Adds one non-empty, extension-unique action with resolvable JSON Schemas. `scope` is exactly `worldless`, `workspace`, or `worker`; the handler receives schema-validated params and a separate matching context. `residency` is explicitly `required` or `none`: only the former acquires workspace capabilities and reconciles worker documents. Worldless actions require `none`. Scoped contexts contain trusted bound identifiers, never client parameters. A client-interface module decides whether and how the name becomes public, validates successful output, and owns collisions with its built-ins. |
4171
4169
  | §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. |
4172
4170
  | §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}). |
4173
- | `readWorkspaceEnvironment(workspaceId)` | Captures the workspace env layer ({§workspace-env}) and returns its composer. No argument uses admitted host values; a supplied environment supplies a module's reference-resolution context. Both apply the same captured values and masks, without worker overrides. |
4174
- | §module-workspace-directory `workspaceStateDirectory(workspaceId, namespaceOwner)` | Returns and creates the module's absolute operational-state directory under the daemon's XDG state home. Core owns path resolution and a stable random workspace storage key in its own `workspace_module_state` row. The key survives workspace renames and daemon restarts; independently created workspaces, including in other databases, receive different keys. The module owns its contents and child-directory lifetimes. |
4175
- | §module-functionality-adapter `registerFunctionalityAdapter(adapter)` | Registers one family beneath the shared coordinator ({§functionality-coordinator}). |
4176
4171
  | §module-workspace-capabilities `replaceWorkspaceCapabilities({ workspaceId, namespaceOwner, state, runtimes })` | Atomically replaces one provider's durable state and runtime/scheme snapshot at the workspace operation boundary. Namespace claims are validated before mutation. Failure restores the prior state and publication. |
4177
4172
 
4173
+ Core keys each {§module-workspace-directory} by a stable random workspace
4174
+ storage key in its own `workspace_module_state` row.
4178
4175
  Module directory allocation is lazy, atomic in SQLite, and independent of the
4179
4176
  project root, daemon CWD, and workspace/worker environment overrides. New
4180
4177
  directories use mode `0700`; existing permissions are not rewritten. Module
@@ -4264,12 +4261,33 @@ Retryability describes the actual failed condition, not its numeric status.
4264
4261
  | Verb | Common contract |
4265
4262
  |---|---|
4266
4263
  | `list` | Project definitions, ownership (`origin`), winning configuration source (`provenance`), enabledness, and published preparation outcome: disabled, dormant, active, unavailable with its Problem, or authorization-required. No credential values. |
4267
- | `discover` | Return inert candidates. Never install, persist, enable, or execute them. |
4264
+ | `discover` | Return inert candidates for the inputs the family serves ({§functionality-discover-advertisement}). Never install, persist, enable, or execute them. |
4268
4265
  | `add` | Admit and persist a local definition, prepare it, and enable it atomically. It may override an inherited definition. Reapplying the same local definition enables it idempotently (200); a different local definition for that alias fails 409 without replacing it. |
4269
4266
  | `enable` | Publish an available definition; retry preparation if unavailable. |
4270
4267
  | `disable` | Withdraw live capability; retain its definition and saved results. |
4271
4268
  | `remove` | Forget the locally owned definition and its enabledness override. Restore any inherited definition with its inherited enabledness. Inherited definitions cannot be removed at this scope. Saved results remain. |
4272
4269
 
4270
+ §functionality-discover-advertisement **A family's `discover` advertises exactly the inputs it
4271
+ serves.** The coordinator builds the family's discover input schema from its declared discovery
4272
+ ({§functionality-discovery-inputs}): the declared inputs with their shared
4273
+ `FunctionalityDiscoverQuery` definitions and no other property (a multi-scope family adds
4274
+ `scope`), and, unless a request naming none lists everything, at least one of them: the one
4275
+ input required, or any of several. The client actions, the manager's tool documents and their
4276
+ schema documents carry that schema. Any other input, or a request naming none, is refused by the
4277
+ shared schema check as 400 `arguments-invalid`, its `errors` naming each field; no family mints
4278
+ its own refusal for an input it does not serve. A family without discovery has no `discover`
4279
+ action, and its manager teaches none. A value of a served input the family cannot use remains the
4280
+ family's own refusal.
4281
+
4282
+ | Family | Inputs | A request naming none |
4283
+ |---|---|---|
4284
+ | `skills` | `source` ({§skills-functionality}) | Refused |
4285
+ | `schedule` | `source` ({§schedule-clock}) | Refused |
4286
+ | `mcp` | `query`, while a registry is configured; otherwise no discovery ({§mcp-registry-discovery}) | Refused |
4287
+ | `a2a` | `source`, `configuration` ({§a2a-functionality}) | Refused |
4288
+ | `env` | `query`, `source` ({§env-functionality}) | The whole catalog |
4289
+ | `members` | `query` ({§members-functionality}) | Refused |
4290
+
4273
4291
  §configuration-definition-resolution **Named resource definitions replace whole;
4274
4292
  independent behavior controls remain independent.** Source readers and scope
4275
4293
  overlays apply the same boundary:
@@ -4309,9 +4327,8 @@ composition boundaries, not arbitrary exceptions:
4309
4327
  | Optional startup integration (hooks, hosted A2A, observability) cannot be configured | Withhold that integration and retain its exact configuration diagnostic. Activate the client interface and unrelated capabilities; never invent a replacement setting. |
4310
4328
  | Invalid default model or child selector | Retain its diagnostic at startup; keep client inspection and explicit selection available. A request relying on the invalid selector fails with `daemon:configuration/configuration-invalid` (503), naming its key. Never substitute another model or silently inherit a child model. |
4311
4329
  | Model construction or endpoint verification fails | Reject selection/use before inference or committing the selection. Keep the client available; a later selection can retry or choose another route. |
4312
- | Invalid effect, file-creation, or loop-default policy | Retain the startup diagnostic. Reject the affected operation or unresolved loop policy as `daemon:configuration/configuration-invalid` (503); no guessed admission policy, external effect, or orphan approval wait. Independent operations and explicitly supplied valid loop policies remain usable. |
4330
+ | Invalid effect, file-creation, or server approval policy | Retain the startup diagnostic. Reject the affected operation as `daemon:configuration/configuration-invalid` (503); no guessed admission policy, external effect, or orphan approval wait. Independent operations remain usable. |
4313
4331
  | Invalid retention configuration | Withhold collection and automatic storage conversion, including shutdown collection. Preserve stored evidence and expose the diagnostic; do not substitute a deletion policy. |
4314
- | Invalid packet configuration or retired capacity knobs | Retain the startup diagnostic and keep client inspection available. Reject affected packet construction before inference with `daemon:configuration/configuration-invalid` (503). Never guess a capacity or projection setting. |
4315
4332
  | Invalid executor construction/probe configuration | Keep the installed declaration and its diagnostic, but no executable instance or output scheme. Other executors and ordinary READ/EDIT remain usable. Invoking the unavailable tag returns its exact 503 configuration Problem. |
4316
4333
  | Invalid execution scheduling, input, or scratch configuration | Diagnose at startup and validate on the affected execution path before admission, stream creation, or external effects. Scratch configuration applies only to resource-backed execution; inline programs remain independent. Never substitute concurrency, timeout, or directory settings. |
4317
4334
  | Invalid model-alias catalog | Fail catalog inspection explicitly; omit its unavailable snapshot rather than report an empty catalog. Exact provider/model selection does not depend on aliases. |
@@ -4325,7 +4342,10 @@ composition boundaries, not arbitrary exceptions:
4325
4342
  | Internal invariant, state, or implementation failure | Preserve the exception; never reclassify it as an operator configuration error. |
4326
4343
 
4327
4344
  Client discovery and passive synchronization report startup diagnostics through the
4328
- existing Notice channel, even without a usable model. The first turn of a drain, and a changed diagnostic
4345
+ existing Notice channel, even without a usable model. Each is a Notice from `engine:configuration`
4346
+ of kind `configuration_unavailable` that names the failed setting's `key` and its `owner`: the
4347
+ setting's reader (such as `retention`, `model` or `executor:<runtime>`), a family, `extensions` for an
4348
+ extension declaration, or a module as `module:<package>`. The first turn of a drain, and a changed diagnostic
4329
4349
  thereafter, reports unresolved configuration to both client and model. An
4330
4350
  unchanged diagnostic is not repeated every turn. Operation failures remain Problems.
4331
4351
 
@@ -4356,32 +4376,6 @@ publication contract, and creates no log entries or model Notices. Published
4356
4376
  failures retain their exact Problems in `list`. Concurrent consumers share the
4357
4377
  workspace activity; a client disconnect does not clear another consumer's work.
4358
4378
 
4359
- §functionality-adapter **An adapter owns protocol truth.** It declares its
4360
- family, namespace owner, definition schema, contributed defaults, discovery,
4361
- admission, preparation, and teardown, and its alias grammar when that is not
4362
- the shared lowercase-hyphen one: the coordinator enforces whichever grammar the
4363
- family declares, at admission, in the service projection, and on persisted
4364
- state, so an environment variable's name is an alias exactly as a skill name
4365
- is. Admission distinguishes explicit client
4366
- actions from model operations where the family contract requires it
4367
- ({§members-model-scope}). Preparation receives each complete definition with optional
4368
- adapter-owned interpretation context. Context is source semantics, not policy or provenance;
4369
- it participates in runtime identity and hot-load comparisons, is never projected as configuration
4370
- or persisted into a workspace override, and cannot survive replacement by a local definition.
4371
- Removing that override restores the current inherited definition and context together.
4372
- Descriptive provenance alone does not change runtime identity. Preparation returns runtimes, documents, per-alias
4373
- outcomes, and a snapshot with `commit`/`abort`. Successful publication commits;
4374
- failure aborts; cooling tears down. Protocol continuations remain ordinary
4375
- module actions. Optional `forget` releases an installed or provisioned
4376
- definition before removal; failure rejects removal. The
4377
- seam's shapes — the identity a verb acts under, its options, definition
4378
- sources, outcomes, preparation, the prepared result and the family handle —
4379
- are declared once in `plurnk-contracts` and imported by core and every
4380
- module; core adds only its own face of the seam, the runtime registration a
4381
- resident family prepares and the scheme facet it may expose.
4382
- An adapter may expose current partial-source `configurationNotices`; these join the ordinary
4383
- workspace diagnostics without preventing independently valid definitions from preparing.
4384
-
4385
4379
  §functionality-hotload **Out-of-band state is admitted before the next turn.** An adapter whose
4386
4380
  `available` reads state that changes outside the daemon, such as skill roots ({§skills-hotload}) or
4387
4381
  installed plugins ({§agent-plugins-hosting}), implements `refreshIfChanged`. Turn admission calls it
@@ -4421,9 +4415,10 @@ its discovery roots explicitly ({§operator-config-real-model-profile}), so
4421
4415
  operator roots do not shape a gate. Root selection does not restrict explicit
4422
4416
  source definitions. Skills mutations change workspace bindings, not these roots
4423
4417
  ({§skills-functionality}); MCP mutations likewise remain workspace-owned. The module setup seam's
4424
- `workspaceConfigurationDirectories` supplies selected `<project>/.agents`,
4425
- `$XDG_CONFIG_HOME/plurnk`, and `~/.agents` directories in precedence order, omitting
4426
- project when no project is bound. Modules own their file formats; core owns discovery roots.
4418
+ `workspacePaths` supplies selected `<project>/.agents`, `$XDG_CONFIG_HOME/plurnk`,
4419
+ and `~/.agents` directories in precedence order with their scopes, omitting project
4420
+ when no project is bound ({§module-workspace-paths}). Modules own their file formats;
4421
+ core owns discovery roots.
4427
4422
 
4428
4423
  An adapter may expose a `scheme` facet beneath its family's runtime namespace
4429
4424
  ({§runtime-resource-binding}). A facet claims a path subtree and is the scheme's
@@ -4464,8 +4459,9 @@ set any name it lists or none of them — it answers which names have a **consum
4464
4459
  a Worker learns the name of a value only the operator can supply. The catalog projects
4465
4460
  declarations, never the host environment, so a credential the operator has filled in appears by
4466
4461
  name with its documentation and an empty value ({§exec-env-scoped}: referred to by name, never
4467
- read). `configuration` is refused: a client's own environment contributing candidates would be a
4468
- second door past the ceiling.
4462
+ read). `source` selects one declaring package, and a request naming neither input is the whole
4463
+ catalog. A client's own environment is no input: contributing candidates, it would be a second
4464
+ door past the ceiling.
4469
4465
 
4470
4466
  The family publishes no process runtimes. Its values are read at the spawn that uses them,
4471
4467
  not from a live process or a cached worker environment.
@@ -4478,11 +4474,12 @@ than capability — with worker overrides above workspace defaults. The adapter
4478
4474
  coordinator keys durable state and the locally-owned `origin`
4479
4475
  by it: a workspace-scoped family's own entries carry origin `workspace`, a worker-scoped
4480
4476
  family's carry `worker`. Origin names ownership, never scope, so a projection never claims the
4481
- workspace set a value one Worker set for itself. Nothing else in the contract varies: the six
4482
- verbs, the two projections, enabledness, and the service-baseline rules are one implementation
4477
+ workspace set a value one Worker set for itself. Nothing else in the contract varies but the
4478
+ discovery inputs a family serves ({§functionality-discover-advertisement}): the verbs, the two
4479
+ projections, enabledness, and the service-baseline rules are one implementation
4483
4480
  across every family, which is what keeps their idioms from drifting apart.
4484
4481
 
4485
- A family projects `<scope>.<family>.<verb>` for each supported scope. The action's
4482
+ A family projects `<scope>.<family>.<verb>` for each verb it serves, in each supported scope. The action's
4486
4483
  context binds that scope; a worker-scoped action also names the Worker. Its durable
4487
4484
  value is the same shape per (worker, family) in `worker_module_state`, read
4488
4485
  at each verb and at each spawn rather than held in the workspace snapshot. Its `list` and
@@ -4517,9 +4514,9 @@ Admission, tools, documents, Turn 0, and client status consume that publication.
4517
4514
  The coordinator's synchronous `publish` participates in the registry commit;
4518
4515
  its returned undo restores the previous view before rollback reconciles documents.
4519
4516
  Neither callback performs fallible I/O. Publication failure restores the preceding
4520
- configuration/runtime snapshot; it cannot roll back effects performed by an
4521
- external installer. Installer failures retain their cause and must not be
4522
- reported as successful configuration changes.
4517
+ configuration/runtime snapshot; it cannot roll back an external effect of preparation,
4518
+ such as a fetched skill source. A failed external effect retains its cause and must not be
4519
+ reported as a successful configuration change.
4523
4520
  `settleFunctionality` joins queued
4524
4521
  publications before inspection or shutdown.
4525
4522
 
@@ -4532,7 +4529,8 @@ definitions add no hot-path teaching. Their exact state and Problem remain
4532
4529
  available through `list`.
4533
4530
 
4534
4531
  §functionality-model-projection **Each family has one workspace manager
4535
- executor.** The six verbs use their actual coordinator schemas and the ordinary
4532
+ executor.** Its verbs — `discover` only where the family serves discovery
4533
+ ({§functionality-discover-advertisement}) — use their actual coordinator schemas and the ordinary
4536
4534
  tool-document machinery. `list`/`discover` are read effects; mutations are host
4537
4535
  effects and use normal proposals. Summary, signatures, and deep docs derive
4538
4536
  from the same registry ({§tools-resource-materialization},
@@ -4584,10 +4582,10 @@ Core's behavior behind them.
4584
4582
  |---------------------------------------------------|----------|---------------|
4585
4583
  | §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. |
4586
4584
  | §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. |
4587
- | §methods-proposal-resolve Proposals | `resolveProposal(logEntryId, resolution)` | Validates and delivers one accept, reject, or cancel decision to the engine. An unknown or already-resolved id fails; the client protocol owns how the decision arrived. |
4585
+ | §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. |
4588
4586
  | §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. |
4589
- | §methods-client-interaction-resolve Client interactions | `resolveClientInteraction(interactionId, resolution)` | Validates and delivers one resolved payload or cancellation. Unknown, ownerless, and already-resolved identities fail before affecting an operation. |
4590
- | §methods-loop-run Loops | `runLoop({ workspaceId, workerId, prompt, source?, maxTurns?, policy?, openPaths?, selector?, childSelector? })` | Validates a model worker and complete loop policy, persists it with the effective turn ceiling, then returns an immediate status-100 acknowledgement with `loopId` and `action`. A trusted adapter may identify the prompt's causal actor with one canonical `source`; ordinary clients cannot author it through their protocol surface. The exact terminal result arrives only through `loop/terminated`; parking and resuming do not replace the loop. |
4587
+ | §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. |
4588
+ | §methods-loop-run Loops | `runLoop({ workspaceId, workerId, 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`. 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. |
4591
4589
  | §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. |
4592
4590
  | §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. |
4593
4591
  | 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}). |
@@ -4599,7 +4597,10 @@ Core's behavior behind them.
4599
4597
  | §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. |
4600
4598
  | §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. |
4601
4599
  | §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. |
4602
- | §methods-conversation-worker Workspace lifecycle | `createConversationWorker({ workspaceId, name? })` | Creates a distinct model-origin root worker with empty history: a fresh conversation over the same world, not a fork or the stable default. |
4600
+ | §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}). |
4601
+ | 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. |
4602
+ | Worker ownership | `claimWorkerOwner({ workspaceId, workerId, owner })` | Claims only runtime-owned work under {§worker-ownership}, returning its effective owner. The owner must already be registered. |
4603
+ | Runtime actor | `ensureRuntimeWorker(workspaceId)` | Returns the workspace's `_plurnk` actor, creating it if absent; it always retains runtime ownership. |
4603
4604
  | Workspace lifecycle | `forkWorker({ workspaceId, workerId, name? })` | Creates a child worker that branches the source worker's history while sharing workspace state. |
4604
4605
  | §methods-workspace-rename Workspace metadata | `renameWorkspace(workspaceId, name)` | Changes only the world's unique mutable name; workers, log, and membership remain intact. |
4605
4606
  | §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`. |
@@ -4619,7 +4620,6 @@ already durable on the loop remains authoritative:
4619
4620
  |-----------------------|------------------------------------------------------|--------------------------------|--------------------------------------|
4620
4621
  | Provider/model | The resolved request selection must still agree. | Fold. | 409 provider conflict. |
4621
4622
  | `maxTurns` | Keep the durable ceiling. | Fold. | 409 turn-ceiling conflict. |
4622
- | `policy` request | Keep the complete durable loop policy. | Fold. | 409 policy conflict. |
4623
4623
 
4624
4624
  The conflict names both selections and directs the caller to cancel or conclude
4625
4625
  the loop before changing configuration. A newly enqueued loop instead persists
@@ -4678,7 +4678,8 @@ independent demands, and harness-authored initialization obeys workspace policy.
4678
4678
  `workspaces.settings.capabilities` is the one mutable access policy under the
4679
4679
  service ceiling. Every actor, including an existing child, uses the current
4680
4680
  workspace policy; neither creation nor delegation snapshots authority. Workers
4681
- own their log and scratchpad, not tools or resource grants. Intrinsic source
4681
+ own their log and scratchpad, not tools or resource grants; their owner decides only
4682
+ which interaction runtimes they are offered ({§owner-interaction-ring}). Intrinsic source
4682
4683
  mutability, workspace separation, and proposal approval remain distinct
4683
4684
  contracts. Input is validated before persistence; malformed stored policy fails
4684
4685
  at its reader with the workspace coordinate and cause.
@@ -4692,7 +4693,8 @@ or observation, existing workers reconcile generated references against it. It r
4692
4693
  conversation worker and returns the complete fresh projection.
4693
4694
 
4694
4695
  §question-tool **The native request-user-input tool.** Core registers one
4695
- in-process `question` runtime at boot. Its body is the MCP2 2026-07-28
4696
+ in-process `question` runtime at boot and offers it only to a worker whose owner
4697
+ would receive it ({§owner-interaction-ring}). Its body is the MCP2 2026-07-28
4696
4698
  form-elicitation shape verbatim — `{ message, requestedSchema }`. An
4697
4699
  optional literal target is a descriptive label only: it neither routes the
4698
4700
  question nor changes the body or recipient. The
@@ -4721,6 +4723,20 @@ weights, and catalog text agree. Turn0 surveys that same catalog. Dispatch evalu
4721
4723
  same descriptor and policy cascade at the operation boundary, never at
4722
4724
  registration; there is no separate per-tool availability system.
4723
4725
 
4726
+ §owner-interaction-ring **An interaction tool is offered only where an answer can come
4727
+ from.** An asking worker adds one ring inside that cascade, derived live from its owner
4728
+ and never stored. It denies each interaction runtime whose tool the owner would not
4729
+ receive under {§client-interaction-routing}; an interaction runtime asks under its own
4730
+ tag, as `question` does.
4731
+
4732
+ | Surface | With the ring |
4733
+ | --- | --- |
4734
+ | Turn 0 survey and the worker's FIND of the shared reserved tree | The runtime's documents are absent, from counts and folder summaries too. |
4735
+ | Dispatch | 403 `capability-denied` naming scope `owner`, with the recovery pinned in {§pinned-wording-core}. |
4736
+ | Workspace capability projection and reserved-tree materialization | Unaffected: they take no worker. |
4737
+
4738
+ A FIND or READ by exact path is not filtered.
4739
+
4724
4740
  §model-catalog **Model discovery is a bounded local projection, not provider
4725
4741
  activity.** Core composes the release-pinned Models.dev snapshot with
4726
4742
  provider-owned `{§model-catalog-readiness}` and {§provider-effort}.
@@ -4859,13 +4875,13 @@ adding a loop to it. LOOK text anchors resolve through the same
4859
4875
  | §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. |
4860
4876
  | §notifications-workspace-created `workspace/created` | `{ id, name, projectRoot }` | A workspace is created. This is the only current global event. |
4861
4877
  | `workspace/preparation` | `{ workspaceId, preparation: FunctionalityPreparationActivity[] }` | Workspace capability preparation changes; snapshot and clearing semantics follow {§functionality-preparation-visibility}. |
4862
- | §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 plugin notification capability does not require it. It carries metadata, not content; consumers read bytes by canonical workspace address. |
4878
+ | §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. |
4863
4879
  | §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. |
4864
4880
  | §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. |
4865
4881
  | §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. |
4866
4882
  | §notifications-reasoning-event `reasoning/event` | `{ workerId, loopId, turnId, modelCallId, requestSequence, phase, delta? }` | A main emission call exposes readable reasoning. Each physical request that emits reasoning owns a distinct positive `requestSequence` and balanced start/content/end stream; opening a retry closes the preceding stream before any retry delta. Only content carries a nonempty exact delta. It is transient presentation evidence, never a log row, Notice, packet field, or BARE/child channel. The settled provider response remains the durable authority. |
4867
4883
 
4868
- §notifications-stream-event-failure-isolation The plugin-facing
4884
+ §notifications-stream-event-failure-isolation The extension-facing
4869
4885
  `NotifyCaps.streamEvent()` remains a synchronous advisory call while core
4870
4886
  resolves its entry identity asynchronously.
4871
4887
 
@@ -4903,7 +4919,7 @@ without an origin filter ({§actor-boundary-isolation}).
4903
4919
  ## §packet-assembly Packet assembly
4904
4920
 
4905
4921
  `PacketBuilder.buildRequestPacket` owns the engine's default ordered section
4906
- list. Trusted scheme plugins may transform that first-class list before it is
4922
+ list. Trusted scheme extensions may transform that first-class list before it is
4907
4923
  rendered or measured; {§context-fit} remains an engine-owned post-build rail.
4908
4924
 
4909
4925
  ```mermaid
@@ -4916,23 +4932,27 @@ flowchart LR
4916
4932
 
4917
4933
  ### §packet-wire-envelope The wire envelope
4918
4934
 
4919
- The packet reaches the provider as a transcript, under the roles the model was tuned on, its bytes unchanged:
4935
+ The packet reaches the provider as two messages. No emission is replayed under `assistant`.
4920
4936
 
4921
4937
  | Message | Role | Content |
4922
4938
  |:--|:--|:--|
4923
4939
  | 1 | `system` | the system slot, as rendered |
4924
- | 2, 4, … | `user` | the user slot's text up to and including the record of the next placed emission that delivers text ({§emission-row}); the first opens with `## Log` |
4925
- | 3, 5, … | `assistant` | that row's frozen emission projection less its NOTE and WAIT blocks, as the worker's own message ({§emission-row}) |
4926
- | last | `user` | the records after the last emission delivered, then the remaining user sections in {§packet-cache-monotone} order; native parts ride here ({§packet-attachment-parts}) |
4927
-
4928
- Only role boundaries are added: joined by blank lines, the user messages are the user slot's
4929
- bytes, in record order. An emission is placed exactly when its row is present in the final log
4930
- section, so curation governs the transcript: a KILLed emission row, or one a trusted transform
4931
- removed ({§packet-plugin-transform}), takes its emission with it, and a log without emission rows
4932
- is one user message. An emission of only NOTE and WAIT delivers nothing, so its record runs on
4933
- into the next user message. The Worker block and the status clump always follow the log, so a
4934
- request never ends on an emission, and the projection refuses one that would. The digest's packet
4935
- artifacts record the sections, and `.wire.json` the messages ({§share-packet-names}).
4940
+ | 2 | `user` | the user sections in {§packet-cache-monotone} order; native parts ({§packet-attachment-parts}) precede the optional final {§previous-emission} section |
4941
+
4942
+ The digest's packet artifacts record the sections, and `.wire.json` reconstructs their text-message
4943
+ envelope ({§share-packet-names}); it is not a serialized HTTP capture.
4944
+
4945
+ ### §previous-emission Complete, optional continuity
4946
+
4947
+ | Concern | Contract |
4948
+ |---|---|
4949
+ | Selection | Only the immediately preceding completed model inference turn in this loop. Its admitted content program must have no syntax errors and its emission row must remain active. Never search backward for an older eligible program. Runtime failures do not disqualify it. |
4950
+ | Absence | No preceding model turn, rejected or syntactically partial emission, empty or reasoning-only turn, or retired emission row means no section. Initialization and other loops supply none. |
4951
+ | Content | The frozen canonical admitted content program, whole: every body, NOTE, WAIT and reply retained. No placeholders, redaction, preview or nested-body interpretation. Reasoning operations remain in their own source and receipts. |
4952
+ | Placement | `## Previous Emission`, singular, is the final user section and final transmission content, after native parts when present. Nothing follows it. It is replaced each turn, never inserted into the reusable log prefix. |
4953
+ | Authority | Core attaches this section after trusted section transforms; the extension seam does not rewrite the frozen program. The active emission row owns its curation lifetime. |
4954
+ | Capacity | At the hard context wall, omit the section whole before suppressing any result body or native part ({§context-own-rows-fit}). Omission lasts through rebuilds of that request; the next request decides afresh. No source, receipt, or stored historical packet is changed. |
4955
+ | Accounting | Its text is charged once within the user slot; the emission row charges only its record. |
4936
4956
 
4937
4957
  ### §packet-cache-monotone Default order and cache locality
4938
4958
 
@@ -4952,18 +4972,19 @@ Conditional absence never reorders the surviving default sections.
4952
4972
  | 10 | user | `budget` | `Context Curation`; omitted when capacity is unknown. |
4953
4973
  | 11 | user | `messages` | `Open Messages`: immutable message addresses and causal sources ({§message-arrival}). |
4954
4974
  | 12 | user | `recap` | Optional authored operational recap. |
4975
+ | 13 | user | `previous-emission` | Complete previous program, if eligible and within capacity ({§previous-emission}); always last. |
4955
4976
 
4956
4977
  The order favors prefix-cache locality where semantics permit: the definition
4957
4978
  and privileged policy lead operator notes, while the append-mostly
4958
- log leads the volatile user-status clump. An emission sits at the row that announces it
4959
- ({§packet-wire-envelope}), so the reusable prefix runs through every retained emission, and
4960
- retiring one breaks the prefix at its row like any other curation. It does **not** claim that every system byte is
4979
+ log leads the volatile user-status clump. The previous program follows that clump, so replacing it
4980
+ does not rewrite the log prefix. Retiring an emission row breaks the prefix at its row like any
4981
+ other curation. It does **not** claim that every system byte is
4961
4982
  immutable or that the complete packet is globally monotone in volatility:
4962
4983
  operator notes and policies can change. Trust is a separate
4963
4984
  admission rule. The system slot contains trusted control-plane material;
4964
4985
  attacker-reachable content stays in the user slot.
4965
4986
 
4966
- ### §packet-plugin-transform Trusted whole-list extension seam
4987
+ ### §packet-extension-transform Trusted whole-list extension seam
4967
4988
 
4968
4989
  `SchemeRegistry.transformSections` pipes the complete default list through
4969
4990
  every registered scheme implementing `transformSections(sections) -> sections`,
@@ -4973,10 +4994,10 @@ Each initial or returned list passes the schemes-owned validator, including
4973
4994
  unique-name enforcement, before the next transformer or renderer. Each
4974
4995
  transformer may inspect the section content and add, remove, or reorder
4975
4996
  sections. It receives no separate engine, database, actor, or request context.
4976
- Emission placement reads the final log section, so a transform that removes an
4977
- emission row's record removes its emission from the transcript ({§packet-wire-envelope}).
4997
+ Core then attaches the optional frozen previous program ({§previous-emission}); transforms own
4998
+ section drafts, not the worker's source evidence.
4978
4999
 
4979
- This is strictly a trusted in-process seam, admitted through the common plugin
5000
+ This is strictly a trusted in-process seam, admitted through the common extension
4980
5001
  trust gate; an external client action cannot invoke it. Whole-list transformation is
4981
5002
  the fork-avoidance valve for alternate packet shapes, while
4982
5003
  overflow recovery and packet projection remain closed engine concerns.
@@ -4988,23 +5009,39 @@ time of measurement.
4988
5009
 
4989
5010
  | Fact | Owner and unit | Time | Contract |
4990
5011
  |:-----|:---------------|:-----|:---------|
4991
- | Core curation weight | `contentWeight = ceil(chars/2)` over channel content, canonical log bodies, rendered packet slots, and placed emissions | Write/build | Stable, model-independent measure; never a tokenizer claim. The model meets it as `tokens`. |
5012
+ | Core curation weight | `contentWeight = ceil(chars/2)` over channel content, canonical log bodies, rendered packet slots (including the optional previous emission) | Write/build | Stable, model-independent measure; never a tokenizer claim. The model meets it as `tokens`. |
4992
5013
  | §tokenomics-context-envelope-admission Provider input capacity | Provider model limits, the configured output reservation and the output floor, in provider tokens | Before every logical request | The reservation `min(maxInputTokens, contextWindow − outputBudget)` sizes the room; the wall `min(maxInputTokens, contextWindow − outputFloor)` decides admission ({§provider-capacity-admission}). The provider alone measures the complete request and admits it — with the grant flexed when the prompt is over the reservation — defers it, or rejects it at the wall. |
4993
5014
  | Provider generation envelope | Provider response grant and optional reasoning subset, in provider tokens | Before every logical request | The reservation includes hidden reasoning; its strict reasoning subset is never additive. The response grant follows {§provider-flexed-allowance}. |
4994
5015
  | Provider usage and cost | Provider-reported input/output/cache/reasoning tokens and monetary evidence | After every physical request | Durable physical-request forensics under {§provider-usage}; never curation state or a preflight estimate. |
4995
5016
 
4996
- - §context-budget **One room.** The budget is the provider's input capacity converted to curation weight under {§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, no preview page, no pressure threshold. Within a loop the room never shrinks: a packet's budget is the greater of the calibrated capacity and the loop's high-water budget for that input capacity, so a refined conversion can enlarge the room but never turns a packet the model already answered into an overflow.
5017
+ §context-admission **One request through the room.** Core fits the packet to the wall on its estimate, marks it over budget, then has the provider measure the exact request before it is sent. Shedding is the same at both measures, and only a packet that cannot fit even as receipts ends its loop.
5018
+
5019
+ ```mermaid
5020
+ flowchart TD
5021
+ build["Build the packet at the budget"] --> wall{"Estimate over the wall?"}
5022
+ wall -- yes --> shed["Shed: the previous program whole, then the newest rows bodiless"]
5023
+ shed --> wall
5024
+ wall -- no --> over{"Over budget, no row yet?"}
5025
+ over -- yes --> row["One over-budget row at the head of the turn"]
5026
+ row --> wall
5027
+ over -- no --> assess{"The provider assesses the exact request"}
5028
+ assess -- "admit or defer" --> send["Send"]
5029
+ assess -- "exact refusal" --> shed
5030
+ shed -. "nothing left to take" .-> overflow["window-overflow 413, no inference request"]
5031
+ ```
5032
+
5033
+ - §context-budget **One room.** The budget is the provider's input capacity through the current 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, no preview page, no pressure threshold. The budget follows the calibration both ways; 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.
4997
5034
  - §context-gauge **One gauge.** Every packet carries one `## Context` section holding one JSON object: `tokens`, the packet's weight as rendered; `budget`, the room; and `largest`, the `PLURNK_SERVICE_BUDGET_LARGEST_ITEMS` heaviest retained log rows as `{path, tokens}` ranked by weight then path. Each log row heading carries its own `tokens`. The gauge is state: it never presents `budget − tokens` as response allowance, it carries no mandate, and it reads the same at one percent as at ninety-nine; over budget, `largest` is as complete as under it, since those rows are the handles the row asks the model to use ({§context-over-budget-row}).
4998
- - §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 head of the emission 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. Three cuts are automatic and each is named where it happens: the first page of what came back unasked in size ({§markerless-first-page}), the cut at the room in the receipt, and an emission row's head ({§emission-row}); nothing else is previewed, projected, withheld or paged on the model's behalf.
5035
+ - §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 previous program is whole or absent ({§previous-emission}), never a cut.
4999
5036
  - §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.
5000
5037
  - §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 recurs on every packet that is over, and 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.
5001
- - §context-wall **Only the window ends a loop.** The wall is the provider's physical input limit: `maxInputTokens`, and the context window less the output floor ({§provider-output-floor}); the packet's tokens are its weight through the calibration factor ({§tokenomics-calibrated-readout}), so an uncalibrated ruler only overestimates. A packet over the wall first lands its newest rows bodiless ({§context-own-rows-fit}); a packet that cannot fit the wall even as receipts terminalizes with the exact `engine/context/window-overflow` 413 Problem without provider I/O. An unknown window has no wall ({§tokenomics-window-unpollable-deliberate}).
5002
- - §context-own-rows-fit **The model's own rows take the same receipt.** When a packet would exceed the wall, its newest rows that still carry a body or a native part — NOTEs, receipts, delivered results, pictures — render bodiless in the fit rule's receipt shape, newest first and only as many as it takes for the packet to fit: the heading, the facts, and `size`, the lines and tokens of what the row stood for. An emission row is never taken, so its head-cut program keeps the turn legible, and a row whose receipt would weigh no less than its body stays whole. Every body stays stored and readable at its address, the verbs restore the room ({§context-verbs}), and each packet decides afresh from its own newest rows. The daemon chooses nothing to summarize and hides nothing silently.
5003
- - §context-output-selection **Retired, column kept.** Migration 6's `output_admission_turn_id` recorded first-presentation admission under the former withholding rule; the rule is gone and the column stays in the schema unread, as released migrations are frozen ({§db-migrations}).
5038
+ - §context-wall **Only the window ends a loop.** The wall is the provider's physical input limit: `maxInputTokens`, and the context window less the output floor ({§provider-output-floor}). A request over the wall sheds ({§context-own-rows-fit}) and is measured again ({§context-wall-measure}); a packet that cannot fit even as receipts ends its loop with the exact `engine/context/window-overflow` 413 Problem, and no inference request is made. An unknown window has no wall ({§tokenomics-window-unpollable-deliberate}).
5039
+ - §context-wall-measure **The wall measures the request that will be sent.** The estimate is the packet's weight through its calibration factor ({§tokenomics-calibrated-readout}) and is never assumed to overestimate. A packet that fits by the estimate is assessed by the provider as the exact wire request ({§provider-capacity-admission}): an admission sends it; an exact refusal is the wall's verdict, and the request sheds by the refusal's excess and is assessed again. A provider that can only estimate defers, and the upstream provider is then the capacity oracle: its refusal ends the loop with the provider's own 413.
5040
+ - §context-own-rows-fit **The model's own rows take the same receipt.** When a request is over the wall by either measure ({§context-wall-measure}), its newest rows that still carry a body or a native part — NOTEs, receipts, delivered results, pictures — render bodiless in the fit rule's receipt shape, newest first and only as many as it takes for the request to fit: the heading, the facts, and `size`, the lines and tokens of what the row stood for. The optional previous program has already been omitted whole ({§previous-emission}); an emission record has no body to take. A row whose receipt would weigh no less than its body stays whole. Every body stays stored and readable at its address, the verbs restore the room ({§context-verbs}), and each packet decides afresh from its own newest rows. The daemon chooses nothing to summarize and hides nothing silently.
5004
5041
  - §tokenomics-weight-stored-at-write **Curation weight, stored at write.** `entry_channels.weight` weighs the complete channel content. `log_entries.weight` weighs the complete canonical `LogBody` content before coordinate and packet presentation; persistence `tx`/`rx` envelopes contribute nothing merely by existing, and proposal settlement recomputes the value when the canonical result changes. Bodyless rows therefore weigh zero. The stored number is a stable content-depth measurement, not a provider-token prediction. `entry_channels.lines` is the channel's line count beside it, a stored generated column SQLite keeps on every write as the persisted mirror of {§logical-line-count} (a trailing newline terminates the last line; empty content has none), so a catalog lists extent without reading bodies.
5005
- - §tokenomics-render-weight-budget **Packet curation budget.** `tokens` measures the *complete assembled packet* after section transforms and readout substitution, its rendered slots and the emissions it places ({§emission-row}); it is not a sum of stored row weights ({§tokenomics-weight-stored-at-write}). Core measures minimum-width probes, monotonically expands fields that do not fit, then right-aligns final values into those widths; final substitution is length-invariant and the displayed total equals the stored request weight. Receipt, FIND-item, pressure-inventory, total, and ceiling figures all use the same curation ruler. A `SUM` of stored content weights measures a different artifact and cannot substitute for packet render weight.
5006
- - §tokenomics-calibrated-readout **Convert capacity, never content costs.** Before packet assembly, Core obtains the answering model's last five settled emission responses pairing a measured packet weight with a provider-reported prompt count. The conversion factor is `sum(reported) / sum(weight)`; fewer than three samples use 1. `floor(inputCapacity / factor)` converts provider capacity into curation units; a loop's budget is that figure or its high-water for the same capacity, whichever is greater ({§context-budget}). Zero means no whole curation unit fits; unknown input capacity remains `null`. The built packet captures this allowance once for its readout, pressure inventory, overflow admission, and persisted client gauge. Later responses cannot change that packet's allowance. Samples are model-keyed, not worker-local; a model with no samples starts at 1. Calibration never changes stored weights, rendered receipt costs, or the immutable request history ({§tokenomics-agnostic-ruler}).
5007
- - §tokenomics-window-partition **One capacity derivation; no service-side token budget.** The provider owns model limits, the configured output reservation, and each call's response grant. Its resolved `inputCapacity` supplies the physical denominator exposed to clients and the boundary conversion into curation units ({§tokenomics-calibrated-readout}). Core shapes context in curation units; provider request-shaped evidence alone admits or rejects physical I/O. `PLURNK_SERVICE_PROMPT_BUDGET`, `PLURNK_SERVICE_SAFETY`, and the additive reasoning/completion reserve knobs are retired; local and custom deployments tune context window, total output budget, optional reasoning subset, and prompt-projection percentage at their owning layers.
5042
+ - §tokenomics-render-weight-budget **Packet curation budget.** `tokens` measures the *complete assembled packet* after section transforms and readout substitution, its rendered slots (including {§previous-emission}) and native attachments; it is not a sum of stored row weights ({§tokenomics-weight-stored-at-write}). Core measures minimum-width probes, monotonically expands fields that do not fit, then right-aligns final values into those widths; final substitution is length-invariant and the displayed total equals the stored request weight. Receipt, FIND-item, pressure-inventory, total, and ceiling figures all use the same curation ruler. A `SUM` of stored content weights measures a different artifact and cannot substitute for packet render weight.
5043
+ - §tokenomics-calibrated-readout **Convert capacity, never content costs.** Before packet assembly, Core obtains the answering model's last five settled emission responses pairing a measured packet weight with a provider-reported prompt count. The conversion factor is `sum(reported) / sum(weight)`; fewer than three samples use 1. `floor(inputCapacity / factor)` converts provider capacity into curation units, the budget ({§context-budget}); the same factor converts the packet's weight into the wall's estimate ({§context-wall-measure}). Zero means no whole curation unit fits; unknown input capacity remains `null`. The built packet captures this allowance and its factor once for its readout, pressure inventory, wall estimate, and persisted client gauge. Later responses cannot change that packet's allowance. Samples are model-keyed, not worker-local; a model with no samples starts at 1. Calibration never changes stored weights, rendered receipt costs, or the immutable request history ({§tokenomics-agnostic-ruler}).
5044
+ - §tokenomics-window-partition **One capacity derivation; no service-side token budget.** The provider owns model limits, the configured output reservation, and each call's response grant. Its resolved `inputCapacity` supplies the physical denominator exposed to clients and the boundary conversion into curation units ({§tokenomics-calibrated-readout}). Core shapes context in curation units; provider request-shaped evidence alone admits or rejects physical I/O. Local and custom deployments tune context window, total output budget and optional reasoning subset at their owning layers.
5008
5045
  - §tokenomics-window-unpollable-deliberate **Unknown provider capacity stays unknown.** When the provider cannot derive `inputCapacity`, there is no budget: the gauge carries `tokens` alone, every result arrives whole, and the provider remains the capacity oracle ({§provider-capacity-admission}). Core never invents a stand-in.
5009
5046
 
5010
5047
  §tokenomics-client-gauge **Clients receive curation and physical occupancy as separate pairs.** `loop/terminated.usage` carries latest packet-bearing model-turn `curationWeight`/`curationBudget` and latest-emission-call `contextTokens`/`contextCapacity`; each unknown fact is `null`. The curation pair is the packet's measured weight and captured allowance, exactly as displayed to the model, not a recalculation using newer usage evidence. Packetless chronology cannot erase an assembled-request gauge. Both physical facts bind to that same call: a preflight rejection may report capacity while its absent physical request leaves `contextTokens=null`, never borrowed from an earlier call. Clients never divide provider-reported physical tokens by Core curation weight. `providers.list` exposes each instantiated alias's `inputCapacity`. A model switch replaces the latest-turn facts together; aggregate provider accounting remains cardinal monetary evidence, not a gauge input.
@@ -5031,7 +5068,7 @@ execute, enter model history, or become a later request's successful output.
5031
5068
  | 200 | Messages answered, results observed, held work settled |
5032
5069
  | 499 | Worker-scope or client cancellation |
5033
5070
  | 429 | Turn allowance exhausted |
5034
- | 413 | A result that does not fit ({§context-fit}), a packet over the budget ({§context-over-budget-row}: a row; the request still goes), a packet over the window ({§context-wall}), or the provider's own rejection of an exact prompt over its wall |
5071
+ | 413 | A result that does not fit ({§context-fit}), a packet over the budget ({§context-over-budget-row}: a row; the request still goes), a packet that cannot fit the window even as receipts ({§context-wall}), or an upstream provider's own refusal ({§context-wall-measure}) |
5035
5072
  | 500 / 508 | Strike threshold or invalid-emission exhaustion / crossing strike caused by a cycle |
5036
5073
  | 504 | Loop timeout or exec-timeout restamp |
5037
5074
 
@@ -5129,8 +5166,8 @@ header. A null header renders only its content. Empty content is omitted, traili
5129
5166
  newlines are removed from each section, and rendered sections are separated by one
5130
5167
  blank line. Any node whose content is empty is absent from the wire, except the
5131
5168
  child-orientation sections, which state emptiness as `[]` ({§packet-empty-sections}).
5132
- Core owns the order at {§packet-cache-monotone}; a trusted plugin may transform the
5133
- section list before rendering ({§packet-plugin-transform}). The projection preserves
5169
+ Core owns the order at {§packet-cache-monotone}; a trusted extension may transform the
5170
+ section list before rendering ({§packet-extension-transform}). The projection preserves
5134
5171
  the evidence section owners supply: paths, URI fragments, log coordinates, scopes and
5135
5172
  coordinate-prefixed body lines remain usable without translation; curation and log-row
5136
5173
  measurements stay attached to what they measure ({§tokenomics-agnostic-ruler});
@@ -5149,9 +5186,10 @@ their boundaries ({§log-wire-format}).
5149
5186
  | `errors` | user | JSON status/log-path pointers | {§operation-results} |
5150
5187
  | `notices` | user | Terse observation bullets | {§notice-drain-on-read} |
5151
5188
  | `git` | user | Working-tree state in a NOTE blockquote | {§packet-cache-monotone} |
5152
- | `budget` | user | JSON curation usage and ceiling; pressure guidance when needed | {§context-gauge} |
5189
+ | `budget` | user | JSON curation usage and ceiling | {§context-gauge} |
5153
5190
  | `messages` | user | JSON pointers to the loop's unanswered immutable messages, path and source | {§message-arrival} |
5154
5191
  | `recap` | user | Optional authored operational recap | {§recap} |
5192
+ | `previous-emission` | user | Complete preceding content program, whole or absent; always last | {§previous-emission} |
5155
5193
 
5156
5194
  §packet-stored-shape **A model packet preserves the rendered request and, only
5157
5195
  when an emission is admitted, its response.** Core assembles and measures the
@@ -5164,7 +5202,7 @@ leaves the request-only record, while rejected exchanges remain in their
5164
5202
  | Turn state | `turns.packet` (the bag) + `turn_sections` rows |
5165
5203
  | ----------------------------- | ----------------------------------------------- |
5166
5204
  | No admitted model request (including initialization and local capacity rejection) | SQL `NULL`, no rows |
5167
- | Request assembled | `{ weight, attributions }` + the sections as items; the emissions it placed stay in their rows ({§emission-row}) |
5205
+ | Request assembled | `{ weight, attributions }` + the sections as items; the complete previous-emission section, or its empty content, is stored with the request ({§previous-emission}) |
5168
5206
  | Response admitted | `{ weight, attributions, assistant, assistantRaw }` + the sections as items |
5169
5207
 
5170
5208
  §packet-items **Sections are rows over content-addressed items; the bag never holds them.**
@@ -5179,7 +5217,7 @@ whose INSTEAD OF trigger refuses a turn that is not an open model inference turn
5179
5217
  items, the composition, the bag, and the provider metadata together. Readers of a whole packet
5180
5218
  select from `turn_packets`, which assembles `sections` back into the bag byte for byte (the digest,
5181
5219
  and every test that inspects a stored packet); statements that need one field read the bag
5182
- directly with `json_extract`. A packet transformed by a plugin, or any non-log section, is one
5220
+ directly with `json_extract`. A packet transformed by an extension, or any non-log section, is one
5183
5221
  item. Items no composition references are transient data: `retention_collect_packet_items`
5184
5222
  collects them under the retention policy ({§retention-policy}), which is how a deleted worker's or
5185
5223
  workspace's packets release their space while shared items survive. A fork copies the composition
@@ -5203,62 +5241,12 @@ operation turns and therefore store `NULL`. Digest projects exact operation
5203
5241
  source independently from this optional model-exchange record; a request-only
5204
5242
  turn receives a note instead of a fabricated response.
5205
5243
 
5206
- §digest-turn-artifact-identity **Digest packet artifacts project durable turns.**
5207
- After selectors are applied, digest retains every turn with exact content or reasoning source, a
5208
- valid stored provider request, or malformed stored packet evidence; orders those
5209
- turns by durable chronology; and names each by its log coordinate ({§share-packet-names}). The
5210
- producer does not affect projection.
5211
-
5212
- §share-packet-names **Packet artifacts carry the coordinate the log uses.** A turn's files are named
5213
- `<worker>-<loop>-<turn>`, the worker's name and the loop and turn sequences that `log:///<loop>/<turn>/…`
5214
- addresses: the model's first turn in its first loop is `<worker>-1-2`, because the initialization
5215
- survey is turn 1 and writes no packet. A digest spanning several workspaces nests each workspace's
5216
- files in a folder named for it; a name that cannot name a file (a workspace named by its path,
5217
- `~/ptl/x`) is slugged for the folder, `ptl-x`, while the digest text keeps the name verbatim, and
5218
- two names that slug alike are told apart by the row's id. `digest.json` records each turn's stem as `artifact`, so no
5219
- consumer reconstructs a name. A name that cannot be a file name, or two turns sharing one, fails.
5220
-
5221
- | Artifact | Present when | Authority |
5222
- |----------|--------------|-----------|
5223
- | `<stem>.assistant.md` | The turn has an `ops` source | Exact `turn_sources.content`, independent of log rows |
5224
- | `<stem>.reasoning.md` | The turn has a `reasoning` source | Exact `turn_sources.content`, without relabeling it as content |
5225
- | `<stem>.system.md`, `<stem>.user.md` | The turn stored a provider request | Stored text sections projected through `PacketWire`; native parts are not Markdown |
5226
- | `<stem>.wire.json` | The turn stored a provider request | The request's text messages in order, its worker's emission rows placed ({§packet-wire-envelope}); `<stem>.wire.invalid.json` names a stored log that cannot be projected |
5227
- | `digest.json` turn `attachments` | Every turn | Stored native attachment descriptors; `[]` means a request without attachments, `null` means no valid stored request. Selection is not proof of provider acceptance. |
5228
- | `<stem>.assistantRaw.json` | The request has an admitted provider response | Stored opaque provider response |
5229
- | `<stem>.response.md`, attempt artifacts | The request received no admitted response | Stored request and attempt state |
5230
- | `<stem>.packet.raw.txt` | The stored packet fails typed validation | Exact stored packet text |
5231
- | `<stem>.packet.invalid.json` | The stored packet fails typed validation | Turn identity and complete validation error chain |
5232
-
5233
- A source-backed turn without provider participation produces only its source-channel
5234
- artifacts; a request-only turn produces no fabricated assistant. A
5235
- source-less programmatic turn with no provider request has no forensic payload
5236
- to project and writes no files.
5237
-
5238
- The external tokenless draft and transformation boundary is owned by
5244
+ he external tokenless draft and transformation boundary is owned by
5239
5245
  {§scheme-packet-transform}. Core alone extends each validated draft with its
5240
5246
  measured `weight` field for storage. #74 tracks coverage that mistakes the sum
5241
5247
  of section weights for the rendered request weight.
5242
5248
 
5243
- §definition-table-projection The authored `plurnk.md` remains human-aligned. Its `definition` section deterministically removes Markdown table-cell padding and shortens separator cells to three dashes before plugin transforms, measurement, storage, and wire rendering; alignment colons survive, while fenced blocks and all non-table whitespace remain exact.
5244
-
5245
- §lexicon **Vocabulary follows the standard of its audience.**
5246
-
5247
- | Layer | Rule |
5248
- |--------------------------|------|
5249
- | Operator, wire, storage | Use the applicable industry term. Provider quantities follow the OpenAI vocabulary where it is standard: `contextWindow`, `reasoning`, `completion`, `finish_reason`, and usage nouns. |
5250
- | Core lifecycle | Use the exact Workspace → Worker → Loop → Turn → Op hierarchy in {§lifecycle-terms}. An AG-UI Run or thread is always protocol-qualified. |
5251
- | Model-facing packet | Use the model's training distribution: operations mirror HTTP and shell, while log records use ordinary Markdown headings, strict JSON metadata, and text coordinates. Renaming this vocabulary to internal API terminology would discard useful resonance for a standard the model never sees. |
5252
-
5253
- | PLURNK-native term | Why it remains |
5254
- |--------------------------------|----------------|
5255
- | `worker` / `loop` / `turn` | The process hierarchy in {§lifecycle-terms}; unqualified `run` names no internal entity. |
5256
- | `packet` | The assembled address space, a kernel concept rather than merely a provider request. |
5257
- | `costUsd` | No standard cost field exists; the explicit currency avoids implied units, and the value remains an exact decimal string. |
5258
- | `curationWeight` / `curationBudget` | Explicitly distinguish Core's model-independent context-shaping facts from physical provider tokens. |
5259
- | the `chars/2` curation ruler | Model-agnostic by design ({§tokenomics-agnostic-ruler}); it is never presented as a tokenizer. |
5260
-
5261
- Retired terms stay retired: the lexicon guard rejects `thinking`, the unqualified `session` noun, `contextSize`, `decodeBudget`, and moved partition-knob names. <!-- lexicon-allow: this sentence enumerates the retired terms -->
5249
+ §definition-table-projection The authored `plurnk.md` remains human-aligned. Its `definition` section deterministically removes Markdown table-cell padding and shortens separator cells to three dashes before extension transforms, measurement, storage, and wire rendering; alignment colons survive, while fenced blocks and all non-table whitespace remain exact.
5262
5250
 
5263
5251
  §body-projection **One full body, one readable view, one packet projection.** Every durable log row has one canonical full body resolved from its stored tx/rx envelope by `LogBody`. READ and FIND over `log:///`, persistent search derivation, and packet rendering apply the same deliberate trimming under {§log-readable-projection}. Packet rendering additionally applies initial suppression and these presentation bounds:
5264
5252
 
@@ -5269,11 +5257,11 @@ Retired terms stay retired: the lexicon guard rejects `thinking`, the unqualifie
5269
5257
  | inbound `SEND` from outside the workspace | the operator's: complete when it fit, otherwise its size and address ({§context-fit}, {§message-projection}); a peer worker's: its first page ({§markerless-first-page}) |
5270
5258
  | structured `EDIT` receipt or textual `COPY`/`MOVE` effects | complete receipt-owned join context |
5271
5259
  | every other nonempty body | the model's own: complete; what it did not author: its first page ({§markerless-first-page}); then {§context-fit} |
5272
- | the `reasoning` row | the model's own reasoning, complete, or no row at all ({§reasoning-row}) |
5260
+ | the `reasoning` row | the optional reasoning preview under {§reasoning-row} |
5273
5261
  | a row the wall took | its size and address, no body and no native part ({§context-own-rows-fit}) |
5274
5262
  | bodyless row | heading and any facts; no coordinate lines; `tokens` includes any selected native part |
5275
5263
 
5276
- §markerless-first-page **What the model did not ask for in size arrives as its first page.** A READ or FIND without a scope, a stream delivered when a command closes, a peer worker's message, an environment delta, an extension's body and a rejected emission shown back all arrive as their first page: `PLURNK_SERVICE_PREVIEW_LINES` lines and at most `PLURNK_SERVICE_PREVIEW_CHARS` characters, in the projection's own units — lines of text, bytes of a byte view ({§read-bytes}), results of a FIND — with one fact in one notation stating the page and the whole, `<1,100> of 4812 lines` (`range` on a retrieval, `preview` on a stored body, {§packet-extent-metadata}). What the model authored stands whole: a NOTE, a WAIT, the body of its SEND, the prompt of its WORK or FORK, and the command it handed an executor. Its own reasoning, when it lands as a row, stands whole only when it fits the row's page and otherwise takes its last page ({§reasoning-row}). What it asked for exactly stands whole: an explicit scope — `<a,b>`, `<1,-1>`, a pattern — and an EDIT receipt, bounded by its own rule ({§edit-result-receipt-truth}). The operator's message keeps its own rule ({§message-projection}) and an emission row its head ({§emission-row}); those are the only exceptions, so the model can reason about every page the same way. A page lands under {§context-fit} like any result: whole when it fits the remaining budget, otherwise the prefix and receipt. Retrievals take their page at the source, before storage, and the packet applies no second cut to a stored result; a stored body takes its page in the packet, and the rest stays readable at the row's address. No operation carries a page size of its own.
5264
+ §markerless-first-page **What the model did not ask for in size arrives as its first page.** A READ or FIND without a scope, a stream delivered when a command closes, a peer worker's message, an environment delta, an extension's body and a rejected emission shown back all arrive as their first page: `PLURNK_SERVICE_PREVIEW_LINES` lines and at most `PLURNK_SERVICE_PREVIEW_CHARS` characters, in the projection's own units — lines of text, bytes of a byte view ({§read-bytes}), results of a FIND — with one fact in one notation stating the page and the whole, `<1,100> of 4812 lines` (`range` on a retrieval, `preview` on a stored body, {§packet-extent-metadata}). What the model authored stands whole: a NOTE, a WAIT, the body of its SEND, the prompt of its WORK or FORK, and the command it handed an executor. Its own reasoning, when it lands as a row, stands whole only when it fits the row's page and otherwise takes its last page ({§reasoning-row}). What it asked for exactly stands whole: an explicit scope — `<a,b>`, `<1,-1>`, a pattern — and an EDIT receipt, bounded by its own rule ({§edit-result-receipt-truth}). The operator's message keeps its own rule ({§message-projection}) and emission rows remain folded ({§emission-row}); those are the only exceptions, so the model can reason about every page the same way. A page lands under {§context-fit} like any result: whole when it fits the remaining budget, otherwise the prefix and receipt. Retrievals take their page at the source, before storage, and the packet applies no second cut to a stored result; a stored body takes its page in the packet, and the rest stays readable at the row's address. No operation carries a page size of its own.
5277
5265
 
5278
5266
  READ and FIND own their range or pagination before packet rendering; the packet never applies a second hidden substring bound to their selected result. Reasoning arrives through ordinary scoped READs ({§reasoning-history}). When a visible projection differs from its canonical body, metadata carries `preview` under {§packet-extent-metadata}; complete and fully suppressed bodies omit it. ```` ```READ (log:///<coordinate>/<OP>) ```` selects untrimmed lines in original coordinates under {§log-readable-projection}; the unsuffixed exact shorthand and authoritative suffix behavior are defined by {§log-coordinate-hierarchy}. ```` ```FIND (log:///...) ```` and search match that same readable view. System/policy sections are not log bodies. Notices are transient non-log observations; they share the ordinary line/character bounds but have no durable body or recovery URI.
5279
5267
 
@@ -5324,7 +5312,7 @@ message — an AG-UI message UUID, an A2A address — stays the durable `path` t
5324
5312
  delivery and reply accounting use, and remains addressable in its own scheme; the short form is an
5325
5313
  additional alias. Answering either reaches the same message.
5326
5314
 
5327
- §message-causal-source **Message authorship and delivery are distinct facts.** The harness publishes every arrival row; the row's `source` carries the canonical address of the causal actor. Native WORK, FORK, and directed worker SEND derive `worker://<sender>` from the authenticated sender worker ID. A trusted exterior adapter supplies its own canonical actor address through {§methods-loop-run}: the AG-UI bridge names the client's message under `agui://` ({§agui-run-source}), the inbound A2A adapter under `a2a://`. An absent source is the operator. Attribution persists with the message through the inbox, parking, orphan recovery, restart, and later log projection; model syntax cannot author it. The wire renders the row's `source` in place of its `origin`, which is constant for every arrival, except where the source is the transport that minted this very message, which says nothing the address does not ({§message-short-identity}). The operator's message is then the one arrival with no sender to show, so it renders `"origin": "user"`; a bare arrival would read as the model's own SEND and hide the request it answers. The Open Messages pointer carries the same attribution ({§message-arrival}).
5315
+ §message-causal-source **Message authorship and delivery are distinct facts.** The harness publishes every arrival row; the row's `source` carries the canonical address of the causal actor. Native WORK, FORK, and directed worker SEND derive `worker://<sender>` from the authenticated sender worker ID. A trusted exterior adapter supplies its own canonical actor address through {§methods-loop-run}: the AG-UI module names the client's message under `agui://` ({§agui-run-source}), the inbound A2A adapter under `a2a://`. An absent source is the operator. Attribution persists with the message through the inbox, parking, orphan recovery, restart, and later log projection; model syntax cannot author it. The wire renders the row's `source` in place of its `origin`, which is constant for every arrival, except where the source is the transport that minted this very message, which says nothing the address does not ({§message-short-identity}). The operator's message is then the one arrival with no sender to show, so it renders `"origin": "user"`; a bare arrival would read as the model's own SEND and hide the request it answers. The Open Messages pointer carries the same attribution ({§message-arrival}).
5328
5316
 
5329
5317
  §message-projection **Message storage is unbounded by model context; its arrival in the packet is not.** Core persists every accepted message completely before packet assembly. An arrival renders complete when it fits the remaining budget ({§context-fit}) and otherwise as its size and address, where a READ of the message's log body, scoped or whole, retrieves it. A peer worker's message is received text and arrives as its first page ({§markerless-first-page}). No share of the capacity is set aside for arrivals, and no knob sizes one.
5330
5318
 
@@ -5350,7 +5338,7 @@ their row shape, and their ordering are ordinary FIND projections owned by
5350
5338
  The model's runtime alert surface has two distinct kinds of information:
5351
5339
 
5352
5340
  - **Turn failures are log items.** A failed action and an engine-rail failure are durable `log_entries` rows whose `rx` is an RFC 9457 operation result. They can be scoped-KILLed, retired, and budgeted like every other row. The `errors` section is a derived pointer index over recent `status_rx ≥ 400` rows; it owns no bodies or failure state. Rejected emissions never become accepted turn content; their private response and admission evidence remains in `model_calls` and `turn_attempts`, apart from the bounded recovery `emissionAttempt` under {§invalid-emission-attempts}.
5353
- - **Notices are transient observations.** Progress and non-fatal diagnostics such as `turn_awaiting_model`, `search_progress`, and `grammar_unenforced` may appear once in the packet and broadcast live. They neither substitute for a failure result nor influence scheduling or recovery.
5341
+ - **Notices are transient observations.** Progress and non-fatal diagnostics such as `turn_awaiting_model`, `search_progress`, and `output_unaccounted` may appear once in the packet and broadcast live. They neither substitute for a failure result nor influence scheduling or recovery.
5354
5342
 
5355
5343
  The `log` is durable product truth. The `errors` section points at its failures
5356
5344
  while the separate `notices` section displays transient observations. The two
@@ -5376,12 +5364,12 @@ retain distinct contracts and lifetimes.
5376
5364
  separate item is minted for operation failures. Actionless engine rails mint
5377
5365
  `op='error'` items because no authored operation row exists. Invalid provider
5378
5366
  emissions are outside this channel because they are not turns. A bare
5379
- failure status, a top-level string `error`, or mismatched result/problem
5367
+ failure status or mismatched result/problem
5380
5368
  statuses violate the producer contract and fail hard. Genuine
5381
5369
  engine-internal faults crash and never mint model-facing rows.
5382
5370
  - **Asynchronous work does not weaken the contract.** A stream-producing operation returns its initial `102` after acquisition. At conclusion the subscription stores the exact universal terminal result; `stream/concluded` carries it unchanged; the next ambient terminal READ merges it with the stream payload and preserves its Problem instance, assigning the committed `log:///.../READ` URI only when absent. Timeouts and service cancellations replace the complete terminal result with a new valid 504/499 Problem—they never mutate a status while retaining a contradictory Problem.
5383
5371
  - **Self-explaining rows.** A problem `title` names the stable class and `detail` states the occurrence-specific cause. Producer-known operands belong in factual extensions. `stage` appears only when neighboring stages imply different recovery; `recovery` states one generally valid next action; `retryable` is true only when the producer recommends automatically retrying the identical request. Unknown recovery or retryability is omitted rather than guessed. General workflow teaching stays in the packet rather than being duplicated into every failure. The runtime-neutral writing contract is owned by `@plurnk/plurnk-contracts`.
5384
- - **Exact Problems cross durable and external boundaries.** Scheme capabilities, proposal application, subscription conclusion, loop settlement, AG-UI, clients, digests, and benchmark records preserve the originating Problem object. The model packet alone derives `{§problem-projection}` without mutating that object. An adapter may add a missing durable `instance`, never replace an existing one; it must not rebuild failure truth from `status`, `detail`, `RUN_ERROR`, a scheduler projection, or a legacy string. A failed boundary without a valid Problem is a contract violation and fails hard.
5372
+ - **Exact Problems cross durable and external boundaries.** Scheme capabilities, proposal application, subscription conclusion, loop settlement, AG-UI, clients, digests, and benchmark records preserve the originating Problem object. The model packet alone derives `{§problem-projection}` without mutating that object. An adapter may add a missing durable `instance`, never replace an existing one; it must not rebuild failure truth from `status`, `detail`, `RUN_ERROR`, or a scheduler projection. A failed boundary without a valid Problem is a contract violation and fails hard.
5385
5373
  - **Caught diagnostics are bounded.** Core-owned Problems may include a bounded preview of a caught runtime diagnostic when it states the occurrence-specific cause. `PLURNK_SERVICE_ERROR_DETAIL_LIMIT` owns that model-facing character bound; complete errors remain in daemon diagnostics. Input validation and stable contract failures do not spend this allowance on implementation text.
5386
5374
  - §notice-drain-on-read **Notices** - the few observations that are not log rows render one terse line under their distinct `## Notices` section, never a JSON dump. Packet rendering normalizes whitespace, bounds the producer message with the shared preview limits, and appends any typed position. The notice buffer drains on read; event Notices appear on at most one packet. Parking preserves pending notices for that loop's next packet; another loop cannot consume them. Terminal cleanup, including cancellation while parked, discards undelivered notices. This buffer is process-local, not restart-persistent. Stateful derivation progress and provider availability coalesce in the buffer, so clients observe every checkpoint live while a later model packet receives only the current state under ordinary level filtering.
5387
5375
  - §rail-accounting-private **Rail accounting is private.** Visibility is owned by {§engine-rails}: the model sees concrete failures from admitted turns, never rejected emissions, attempt counts, the strike streak, or cycle detection. Surfacing internal state creates a gamification surface where the model optimizes for engine metrics instead of the task.
@@ -5396,12 +5384,12 @@ retain distinct contracts and lifetimes.
5396
5384
 
5397
5385
  | notice `kind` | Source | Position |
5398
5386
  |---|---|---|
5399
- | `grammar_unenforced` | engine rail verdict, or a forwarded provider transport anomaly such as a discarded-channel escape | content-offset when the observed position maps into content; none for a reasoning-prefix divergence |
5387
+ | `output_unaccounted` | a provider whose billed output tokens exceed those its content and reasoning hold | content-offset at the end of the content |
5400
5388
  | `parse_advisory` | grammar parser — recoverable near-miss which did not invalidate the parsed statements | content-offset into the model's emission |
5401
5389
  | `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 |
5402
5390
  | `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 |
5403
5391
 
5404
- §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 `grammar_unenforced` is `warn`; ordinary lifecycle and progress notices are `info`. Clients color straight off `level` without interpreting the open `kind` vocabulary.
5392
+ §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.
5405
5393
 
5406
5394
  §operation-result-no-error-scheme Private strike and cycle accounting stays engine-internal ({§rail-accounting-private}). Every failure within an accepted turn - a bounded parse error, failed action, or engine rail - is a LOG ITEM (`log:///<coord>`, `status_rx ≥ 400`) with Problem Details, independently curatable and exactly READable while active. The `errors` section surfaces a derived pointer to each. Rejected emissions stay in the forensic model-call and admission relations. There is **no bespoke `error://` scheme** and no ephemeral per-category failure buffer.
5407
5395
 
@@ -5424,83 +5412,14 @@ the same deadline through {§application-loop-observation}, not a restarted cloc
5424
5412
 
5425
5413
  §share **A share is the database's record, ready to send.** `plurnk-service share [<file.db>] [<folder>]`, and `npm run share` from a checkout, take a consistent copy of the database (`VACUUM INTO`; a live database is never read in place), and write its digest into `<folder>`, an ordinary folder the user archives or attaches however they like. Without a database the service's own is shared. Nothing is overwritten: a folder that exists and is not empty is refused, and a caller reusing a place removes it first. The share is the user's bug report and our dogfood, benchmark and forensics artifact alike.
5426
5414
 
5427
- §share-snapshot **A database is copied by SQLite, never by the filesystem.** `Share.snapshot(dbPath, copy)`, exported as `@plurnk/plurnk-service/share` with `Share.write`, is the one consistent copy: a byte copy of a WAL-mode database drops every committed page still in its `-wal` file. A harness that keeps the database beside its digest takes it through `snapshot`; an existing `copy` is refused.
5428
-
5429
5415
  §share-scope **A share is unredacted.** `--workspace=<id>` limits a share to one workspace; without it the whole database is shared. Nothing is filtered, redacted or scanned: a share holds what the models saw and wrote in scope, including prompts, file contents read, command output and reasoning, and the command says so. `--requiem` adds the forensic interview ({§digest-requiem}), which calls a model; `plurnk-service requiem <file.db> <folder>` adds it later to a digest already written.
5430
5416
 
5431
5417
  §share-folder **Shares land in one place.** With no folder named, a share is a stamped child, `share-<UTC stamp>`, of `PLURNK_SERVICE_SHARE_FOLDER` (a leading `~/` expands, as for every explicit Plurnk path) or of `$XDG_STATE_HOME/plurnk/shares`.
5432
5418
 
5433
- §digest-programmatic-surface **The digest is an importable forensic surface.**
5434
-
5435
- | Surface | Contract |
5436
- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
5437
- | Import `@plurnk/plurnk-service/digest` | Ships `Digest` and its package-owned SqlRite statements; importing performs no I/O or process action. The CLI wrapper alone invokes it. |
5438
- | `run({ dbPath })` | Reads the required database and writes a complete digest to `./test/digest` relative to the caller's working directory. |
5439
- | `digestDir` | Selects a nonempty output path. `run` refuses a folder that exists and is not empty, and `requiem` refuses an existing `requiem.json` or `requiem.md`, before database or provider I/O; neither deletes ({§share}). Concurrent callers use distinct folders. |
5440
- | Reader lifetime | `run` reads heavy evidence on demand while rendering, then closes its reader on success or failure. `requiem` closes its reader before awaiting witness inference. |
5441
- | §candidate-pinned-runtime Candidate runtime | 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. |
5442
- | Export completion | Packet and response bodies are read and serialized one record at a time, without discarding evidence. `digest.json` is promoted from a partial file only after every artifact is written; its absence identifies an incomplete export. |
5443
- | `workerId` | Narrows workers and every dependent loop, turn, turn-attached logical inference, specialization, physical request, and log row to that one worker. |
5444
- | `workspaceId` | Narrows workers plus every logical inference and dependent evidence owned by one workspace, when both selectors are present they intersect. |
5445
-
5446
- §digest-cost-kind **Cost basis named.** A rendered Cost line carries the basis of its dollar figure: `(charged)` only when every settled request's cost is provider-charged; `(estimated — catalog rates)` when any settled request's cost is an estimate, because a mixed sum is no more trustworthy than its weakest term. A dollar figure without its basis reads as billed truth, and an estimate must never impersonate a charge.
5419
+ §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.
5447
5420
 
5448
5421
  §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.
5449
5422
 
5450
- §digest-wire-line **Wire health aggregated.** Each worker summary renders a `Wire:` line — total physical provider requests, error-outcome count, and the error percentage when nonzero. Provider-level failures are absorbed by retries below the packet stream, so without this aggregate a rate-limit storm is invisible in every summary while the model's experience stays clean.
5451
-
5452
- §digest-cache-ledger **Measured cache reuse and estimated prompt overlap are separate.**
5453
-
5454
- | Projection | Meaning |
5455
- |---|---|
5456
- | `digest.json` provider-request `cachedTokens`, `inputTokens` | Exact provider-reported cache reads and input tokens; absent quantities remain `null`, reported zero remains zero. Every physical request counts, including retries and first requests of new loops. Stored packet availability is irrelevant to these counters. |
5457
- | Turn `cache=<cached>/<input>` | Sum each measured quantity over that turn's requests. If any request omits a quantity, that sum is `?`. |
5458
- | Workspace `Cache: <cached> of <input> reported input tokens read from cache (<pct>%) over <n> requests` | Sum only requests reporting both counters. Percentage is cache reads / input, rounded to one decimal; zero input is `n/a`. Requests missing either counter are counted separately as `missing input or cache usage (excluded)`. |
5459
- | `digest.json` provider-request `adjacentPrefixTokensEstimate` | Optional loop-local diagnostic: the longest common character prefix with the preceding request, weighted under {§tokenomics-agnostic-ruler} as a share of the current stored prompt, multiplied by reported input tokens. First request: `0`; missing current/preceding packet or current input: `null`. Empty prompts have zero overlap. |
5460
-
5461
- The prefix estimate uses the stored emission packet's wire message order, roles
5462
- and content. A BARE request's input is not that packet; its prefix estimate and
5463
- the following request's comparison are unknown. The estimate is
5464
- neither provider tokenization nor a cache ceiling, and never supplies a cache-ratio
5465
- denominator. Caching across loops or against other provider-resident prefixes
5466
- does not make the measured counters inconsistent.
5467
-
5468
- §digest-edit-census **Every model EDIT by the form it authored, how it landed, and whether it came back.** For each worker the digest reads every model-authored EDIT row and classifies the form from the row's stored marker and the durable statement's pattern: `hash` (one anchor), `line` (one line number), `range` (two marks), `insert` (the zero-width `<L,1,L,1>` form, {§zero-width-column-one-insert}), `column` (any other four-mark region), `prepend` / `append` (`<0>` / `<-1>`), `offset` (a tolerated anchor offset, {§anchor-offset}), `pattern` (a selection matcher), `whole` (no marker: a creation when it lands 201). It counts the EDITs, those refused (status ≥ 400), and the *revisits*: an EDIT of a path the same worker had edited within its previous two model turns — the shape of a repair without the claim of one. Each worker summary renders `EDITs: <n> · <form>=<count>… · refused=<k> · revisits=<r>` (`(no edits)` for none); `digest.json` carries the census as `edit_census` on every worker and stamps every EDIT log entry with its `edit_form` and `edit_revisit`. A form is a fact about what was written, never about intent; the bench sheet reads the counts as friction and leaves the judgement to the reader.
5469
-
5470
- §digest-forensic-fidelity **Forensic fidelity and cardinality.** The digest's machine-readable JSON preserves every log event with its initial and current projection, causal `source`, and structured `attrs`; every exact log-KILL target effect; the exact Problem on every failed row; each loop's exact terminal result, settlement time, scheduled due time, recurring interval, and recurrence lineage; and every ordered physical provider request. Programs still produce chronological `assistant.md` artifacts after every READ receipt is KILLed; source is independent of log curation. Each worker summary's `Emissions:` line counts its announced emission rows, those the worker KILLed, and the headings it echoed ({§emission-row}); its `Reasonings:` line counts its landed reasoning rows, those the worker KILLed, and the turns that reasoned ({§reasoning-row}); the op mix leaves both harness rows out. Each stored packet validates independently: one malformed historical packet remains exact raw evidence with its complete validation error chain and never prevents healthy turns from being projected. Accounting on broader rows is the shared exact derivation from that ledger, never a second stored fact. A worker's Cost line names how many settled requests carry no usage at all (errored or aborted exchanges) — their server-side spend is unrecorded rather than silently priced as zero. The reasoning chronology distinguishes readable reasoning content from provider-reported reasoning usage: when tokens were reported but no readable content was returned, it states both facts instead of implying that no reasoning occurred. The human Markdown waterfall shows a present causal source and may preview only the Problem detail because it remains a triage projection, not the machine record. Targets reconstruct the model-visible address, including hostname, port, serialized query, and fragment; an authority-bearing URL must never degrade from `https://host/path` to `https:///path`, and durable resource coordinates render back to their authority form. Its human Markdown waterfall groups consecutive identical per-turn op outcomes and typed `entry_materialized` narrations, reporting the exact count and sequence span (`xN (seq A-B)`). Grouping keys include source and the complete target, so distinct causes, authorities, or channels never collapse, and non-consecutive events preserve their chronological order. Thus amplification is conspicuous without making the diagnostic artifact itself pathological; valid packet files remain byte-identical records of what the model saw.
5471
-
5472
- Unrecognized actionless log rows are retained and labelled as such, not
5473
- interpreted as executable turnOps or allowed to prevent the remaining digest.
5474
-
5475
- §digest-executor-evidence **A red command is work, not a defect.** Engine-materialized
5476
- completion rows for a failed command carry the executor's problem identity
5477
- (`https://problems.plurnk.xyz/executor/*`), and the digest classifies them as
5478
- evidence: they render like any row but never count toward a loop's error total,
5479
- its health verdict, or the per-turn `errs=` badge — a loop that concluded green
5480
- over red test runs is CLEAN, not DEGENERATE-WIN. This is the digest mirror of the
5481
- strike rail's exemption ({§engine-rails}, #425 F1): structural violations count,
5482
- executor evidence never does.
5483
-
5484
- §digest-requiem **A requiem is an out-of-band forensic interview, not a worker
5485
- turn.** It cannot execute operations or alter the audited history.
5486
-
5487
- | Aspect | Contract |
5488
- |-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
5489
- | Scope | One interview for each worker with model-bearing inference turns; workers without inference evidence are omitted. |
5490
- | Evidence | The worker's final packet plus every attempt's exact normalized response and admission evidence; opaque raw transport remains in durable forensic artifacts. Quoted evidence is budgeted to the witness window ({§digest-requiem-evidence-budget}). |
5491
- | Witness | An explicitly supplied provider or the active configured provider; absence fails hard. |
5492
- | Identity | The worker's durable provider identity ({§worker-provider-identity}) is sent as the `workerId`, without asserting a live worker topology. |
5493
- | Attempts | One call at `PLURNK_SERVICE_REQUIEM_MAX_TOKENS`; only an empty length-limited response receives one retry at `PLURNK_SERVICE_REQUIEM_RETRY_MAX_TOKENS`. |
5494
- | Artifacts | `requiem.md` carries testimony and exact nullable USD accounting. `requiem.json` is durably materialized before each call and preserves logical call state, messages, normalized responses, every physical request's state and accounting, and their shared aggregate projection. |
5495
-
5496
- §digest-requiem-evidence-budget **Quoted evidence fits the witness.** The
5497
- interview's user message is budgeted against the witness provider's context
5498
- window minus the retry output allowance and system framing (chars/2, the
5499
- capacity gate's own estimator). Overflow elides the oldest provider attempts
5500
- behind an explicit `elidedOldestAttempts` count marker, never silently; the
5501
- final packet and the newest attempts always testify. A windowless witness
5502
- (`contextWindow` null) quotes unbudgeted.
5503
-
5504
5423
  §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}).
5505
5424
 
5506
5425
  §turn-accounting-notice **The completion beat carries the spend.** `turn_generated`
@@ -5509,7 +5428,7 @@ USD and token totals with separately named known subtotals across every physical
5509
5428
  calls included. It is the shared exact derivation from the ledger, never a second
5510
5429
  stored fact, so a live watcher accrues running loop cost per turn (#465).
5511
5430
 
5512
- §notice-content-offset-pointer **Content-offset position.** A non-fatal diagnosis on an accepted emission (for example `grammar_unenforced` or `parse_advisory`) carries `position: { type: "content-offset", line, column }` into the model's exact `ops://<worker>/<loop>/<turn>` source; the transcript shows that turn's canonical emission instead ({§emission-row}), so the position names a line of the source, which READ of its address shows. A bounded hard parse error becomes a durable failed operation whose Problem Details preserve its line, column, source, and parser-owned diagnostic. Hard errors that make the frame untrustworthy remain only with their rejected forensic attempt.
5431
+ §notice-content-offset-pointer **Content-offset position.** A non-fatal diagnosis on an accepted emission (for example `output_unaccounted` or `parse_advisory`) carries `position: { type: "content-offset", line, column }` into the model's exact `ops://<worker>/<loop>/<turn>` source; the optional previous-emission section shows a canonical program ({§previous-emission}), so the position names a line of the source, which READ of its address shows. A bounded hard parse error becomes a durable failed operation whose Problem Details preserve its line, column, source, and parser-owned diagnostic. Hard errors that make the frame untrustworthy remain only with their rejected forensic attempt.
5513
5432
  Reasoning normalization notices use source `grammar:reasoning` and omit the
5514
5433
  content-offset position: reasoning coordinates do not address the content source.
5515
5434
 
@@ -5518,9 +5437,9 @@ content-offset position: reasoning coordinates do not address the content source
5518
5437
  §tools-resource-discovery **Executable capability discovery uses ordinary
5519
5438
  Plurnk resources.** No generated tool table rides the system packet. Every
5520
5439
  runtime enabled for the current worker with an admitted invocation materializes one
5521
- family document at `worker:///_plurnk/plurnk/<runtime>.md`. A general runtime's
5440
+ runtime document at `worker:///_plurnk/plurnk/<runtime>.md`. A general runtime's
5522
5441
  document contains its {§executor-tool-document}; a runtime with an exact
5523
- {§executor-tool-registry} materializes a compact catalog at the same address. The family document summarizes
5442
+ {§executor-tool-registry} materializes a compact catalog at the same address. The runtime document summarizes
5524
5443
  the server or runtime, lists every enabled target as a directly copyable
5525
5444
  executable fence named for the runtime, with its input preview ({§operation-aside} carries the
5526
5445
  target one-liner; no invocation dispatch would reject is ever advertised).
@@ -5529,15 +5448,15 @@ Schema-backed targets link from that aside to
5529
5448
  preserve the full tool description and raw input schema under
5530
5449
  {§executor-input-schema-preview}; their nested paths do not contribute extra
5531
5450
  Turn0 rows. A schema-backed general runtime uses `<runtime>/input.md`.
5532
- Non-schema targets retain supplemental details in family sections.
5451
+ Non-schema targets retain supplemental details after the framework-owned sections.
5533
5452
  Tool-result/output schemas remain ordinary evidence, not teaching. Unknown-target
5534
- recovery names the published family document through the same path owner as
5453
+ recovery names the published runtime document through the same path owner as
5535
5454
  materialization, including a runtime's declared `resourcesPath`.
5536
5455
 
5537
5456
  ```mermaid
5538
5457
  flowchart LR
5539
- Survey["Turn 0 FIND<br/>tools/*.md"] --> Families["family paths + summaries"]
5540
- Families --> Read["READ selected family<br/>only when needed"]
5458
+ Survey["Turn 0 FIND<br/>tools/*.md"] --> Runtimes["runtime paths + summaries"]
5459
+ Runtimes --> Read["READ selected runtime<br/>only when needed"]
5541
5460
  Read --> Exec["execution with an aside"]
5542
5461
  Read --> Schema["READ linked input schema<br/>when the preview is insufficient"]
5543
5462
  Schema --> Exec
@@ -5546,28 +5465,28 @@ flowchart LR
5546
5465
  §tools-resource-materialization The runtime registry, workspace capability policy,
5547
5466
  tool resources, and dispatch use one effective workspace snapshot. A
5548
5467
  disabled, unavailable, detached, replaced, or removed runtime has no
5549
- tool resource; an exact registry's empty set publishes no executable family and
5468
+ tool resource; an exact registry's empty set publishes no runtime and
5550
5469
  admits no invocation. Reconciliation deletes stale documents
5551
5470
  before upserting the current set. `PLURNK_SERVICE_DOCS_EXCLUDE` does not hide an
5552
5471
  enabled executable; executor enablement is the sole user-configured filter
5553
5472
  shared by discovery and dispatch. A runtime declaration may carry
5554
5473
  `resourcesPath` — its generated-doc root relative to the workspace's generated
5555
5474
  subtree ({§worker-generated-subtree}). Absent, its docs live in the internal
5556
- `_plurnk/plurnk` namespace; present (attached MCP families: `/tools`),
5557
- the family document materializes at `_plurnk` + that root in the
5558
- shared scratch. Turn 0 surveys the families (`FIND
5475
+ `_plurnk/plurnk` namespace; present (attached MCP servers: `/tools`),
5476
+ the runtime document materializes at `_plurnk` + that root in the
5477
+ shared scratch. Turn 0 surveys the runtimes (`FIND
5559
5478
  (worker:///_plurnk/tools/*.md)`, one row per
5560
5479
  server carrying its summary) and, for each server named in
5561
- `PLURNK_MCP_EXPANDED`, adds one FIND over its family document matching the
5480
+ `PLURNK_MCP_EXPANDED`, adds one FIND over its runtime document matching the
5562
5481
  complete executable blocks (`FIND (worker:///_plurnk/tools/<server>.md)`
5563
5482
  with a multiline regex over matching fences), so turn 0 names every tool with its aside and
5564
5483
  signature — one row per tool, paged like every survey. Capability attenuation
5565
5484
  restricts that matcher to the admitted exact tools. No document is delivered
5566
5485
  unasked.
5567
- When the registry supplies {§executor-tool-catalog} definitions, the family
5486
+ When the registry supplies {§executor-tool-catalog} definitions, the runtime
5568
5487
  document links to its sibling `<runtime>.json`. The catalog contains exactly
5569
5488
  the effective tools and is an ordinary `application/json` resource for READ,
5570
- FIND, and JSONPath selection. It follows the same reconciliation as the family
5489
+ FIND, and JSONPath selection. It follows the same reconciliation as the runtime
5571
5490
  document and per-tool schemas; neither the catalog nor its definitions are
5572
5491
  automatically included in turn 0 or expanded-tool surveys.
5573
5492
  Attached tools are capabilities like every other runtime; the model never
@@ -5599,10 +5518,11 @@ The alias is a short name, suggested from the glob. `list` shows each
5599
5518
  definition with what it resolved to — `include` or `exclude`, the pattern, the members it
5600
5519
  admits or removes (count and a bounded sample), and for a model's inclusion the matches the
5601
5520
  repository's ignore rules refused — so the model sees what its glob did and adapts.
5602
- `discover` is introspection, never a catalog: a path answers why it is or is not visible
5603
- (tracked, included by which pattern, a creation record, excluded by which `!glob`, ignored,
5604
- untracked, absent); a glob previews what `add` would include or exclude. Names only, never
5605
- content; nothing is added.
5521
+ `discover` is introspection, never a catalog: its one input, `query`, is a path or a glob. A path
5522
+ answers why it is or is not visible (tracked, included by which pattern, a creation record,
5523
+ excluded by which `!glob`, ignored, untracked, absent); a glob previews what `add` would include
5524
+ or exclude. A query that names neither, blank or a bare `!`, is 400 `query-invalid`. Names only,
5525
+ never content; nothing is added.
5606
5526
 
5607
5527
  §members-configuration *Available definitions.* `PLURNK_MEMBERS_<alias>=<glob>`
5608
5528
  declares one service-origin rule (`!glob` excludes). The shared naming and
@@ -5638,154 +5558,26 @@ definitions ({§actor-boundary-catalog-preview}), so the model sees why a file i
5638
5558
  not a member before it asks. There is no other membership path: the client's `/members`
5639
5559
  verbs are these verbs.
5640
5560
 
5641
- §skills-functionality **Agent Skills are one workspace Functionality family.**
5642
- Core registers the `skills` family with the coordinator ({§functionality-coordinator});
5643
- its adapter owns protocol truth for standard Agent Skills and nothing else. A
5644
- definition is `SkillDefinition`: the standard skill `name`, its `source`, and
5645
- optional Git `ref`/resolved `commit` ({§skills-sources}). Only a host-provided
5646
- resource tree omits `source`. Source location is not mutation ownership: standard
5647
- project/global roots are read-only configuration inputs; live changes belong to
5648
- the workspace. Plurnk neither installs into nor deletes from those roots.
5649
-
5650
- *Available definitions.* The filesystem is the only truth about installation:
5651
- every `<root>/<name>/SKILL.md` directory under the project, plurnk, then global
5652
- root is one service-origin definition, a nearer root shadowing a farther one and
5653
- all of them shadowing host-provided trees by name. Each filesystem definition
5654
- names its actual source directory. The workspace's durable state owns
5655
- enablement ({§functionality-state}); a disabled skill stays client-visible and
5656
- leaves no model-facing trace.
5657
-
5658
- §skills-configuration **Skills use the shared definition cascade.**
5659
-
5660
- | Layer, low to high | Definition source |
5661
- |---|---|
5662
- | Service | Host-provided trees |
5663
- | Standard locations | Global, plurnk, then project roots selected by {§agent-roots} |
5664
- | Cascading environment | `PLURNK_SKILLS_<name>={"name":"<name>","source":"…","ref"?:"…"}` replaces the complete definition |
5665
- | Live workspace | `skills (add)` creates a workspace definition through {§functionality-coordinator} |
5666
-
5667
- `PLURNK_SKILLS_ENABLED` supplies default enabledness; `<name>_ENABLED` overrides
5668
- it independently ({§resource-environment}). The decoded environment alias must
5669
- equal the standard skill name, including digit-leading and Unicode names.
5670
- Blank definitions are invalid; controls may precede a definition. Environment
5671
- validation checks shape, names, remote URL rules, and Git-only `ref` without
5672
- fetching or opening a source; `commit` is service-recorded, not an input.
5673
-
5674
- *Discovery is inert.* `discover {source}` lists the standard skills one source
5675
- carries, each a candidate with `source` provenance and the exact definition to
5676
- add; it never installs, persists, or enables. Agent Skills have no standard
5677
- registry, so `discover {query}` is 400 `query-unsupported`, naming the source
5678
- forms. Client `configuration` contributes nothing and is refused with 400.
5679
-
5680
- *Admission.* `add {alias, definition}` requires `alias = name` and a `source`;
5681
- the workspace definition may shadow a service skill of the same name. Relative
5682
- sources require a project root; absolute sources work in headless workspaces.
5683
- A local source is recorded as its absolute path.
5684
- A git source records the `commit` its `ref` names at admission, or its default
5685
- branch's when no `ref` is given; `ref` belongs to git sources, and a supplied
5686
- `commit` is refused because the service records it. The family's aliases use
5687
- the standard skill-name grammar ({§agent-skills-name}), including digit-leading
5688
- and Unicode names, rather than the coordinator's generic default.
5689
-
5690
- *Preparation.* For each enabled alias the adapter selects the host-provided
5691
- tree or resolves the complete source definition ({§skills-sources}). Local
5692
- folders and their `SKILL.md` files remain live references, including supporting
5693
- resources and symlink retargeting. Git/archive sources are materialized only
5694
- inside {§module-workspace-directory}; different workspaces and complete source
5695
- definitions cannot reuse each other's materializations accidentally.
5696
- The first fetched Git/archive copy remains stable across enable, cooling, and
5697
- restart until the complete source definition changes. In particular, an
5698
- operator-configured symbolic Git ref is not an implicit update subscription.
5699
- Each admitted skill requires standard `name` and `description` frontmatter
5700
- with `name` matching its directory. A missing, uninstallable, or invalid skill
5701
- is `unavailable` with its exact Problem ({§problems-functionality}) under the
5702
- coordinator's failure policy ({§functionality-model-mutation}); one bad skill
5703
- never fails the family.
5704
-
5705
- Removal follows {§skills-remove}.
5706
-
5707
- §skills-sources **A source is a git remote, a folder, or a file, read with standard tools.**
5708
- Fetching runs nothing it fetched. Materialized copies cannot contain references
5709
- outside their skill; live resources retain {§agent-skills-directory} containment.
5710
-
5711
- | Source | How it is read |
5712
- |---|---|
5713
- | Git remote: a full `https://` or `ssh://` URL, or `user@host:path` | `git ls-remote` resolves the ref at admission; preparation shallow-clones it with hooks and submodules off, and a checkout at any other commit is 409 `source-moved` |
5714
- | Folder: absolute, `~/`, or relative to the project root | Live reference; standard directory/name matching applies |
5715
- | A file named `SKILL.md` | Live reference to its skill directory and supporting resources |
5716
- | A `.zip`, `.tar`, `.tgz`, `.tar.gz`, `.tar.bz2`, `.tar.xz`, or `.tar.zst` archive | Unpacked into private staging with `unzip` or `tar`; a lone top-level directory is the source's root |
5717
-
5718
- Any other scheme, plain `http`, `owner/repo` shorthand, and an https URL carrying
5719
- credentials are refused with `source-invalid` or `source-missing`: shorthand names no
5720
- forge, and a recorded source is listed to every client. Git runs with the
5721
- operator's configuration, credentials, and SSH agent, never plurnk's secrets, and
5722
- never prompts; `PLURNK_SERVICE_SKILLS_FETCH_TIMEOUT_MS` bounds each fetch. The
5723
- retired vendor-installer knobs (`PLURNK_SERVICE_SKILLS_CLI`, `_CLI_TIMEOUT_MS`,
5724
- `_REGISTRY_URL`, `_REGISTRY_LIMIT`, `_REGISTRY_TIMEOUT_MS`) make the skills family
5725
- unavailable when set, each naming what replaced it ({§configuration-repair-path}).
5726
-
5727
- A source's skills are the directories holding a `SKILL.md`, found by walking
5728
- from its root without entering `.git` or a skill already found. A fetched skill at
5729
- the root is named by its frontmatter ({§agent-skills-name}); local references and
5730
- directories below the root use the standard folder rule. A source with `plugin.json` at its root is an
5731
- Agent Plugin and is refused with 422 `source-is-plugin`, so its skills keep the
5732
- plugin's identity. Materialization copies the named skill beside its workspace-owned destination
5733
- and renames it to `<root>/<name>`; a copy that holds a link out of the skill, or
5734
- anything but files, directories, and inward links, is refused with 422
5735
- `source-unsafe` and leaves nothing behind.
5736
-
5737
- §skills-resources **A skill is a resource tree, not a rewritten document.**
5738
- The family exposes enabled, available {§agent-skills-tree} sources through
5739
- `skill://<name>/`. The source owns its bytes; commons entries are demand-loaded
5740
- projections, not writable installations. Filesystem skills retain their original
5741
- directories; service-provided trees need no generated filesystem directory.
5742
- The authority is the name's WHATWG URI representation, including percent-encoding
5743
- for non-ASCII names. Installation names remain unchanged; a raw spelling and its
5744
- serialized URI address the same resource, not separate skill identities.
5745
-
5746
- | Operation | Contract |
5747
- | --- | --- |
5748
- | Turn0 `FIND (skill://*/SKILL.md)` | One ordinary resource catalog: name, address, and standard frontmatter description via {§mimetype-summary}. No synthetic index, body injection, or execution. |
5749
- | `READ (skill://<name>/SKILL.md)` | Original frontmatter and Markdown, unchanged; relative links remain relative to the source layout. |
5750
- | `READ` / `FIND` below the authority | References, scripts, and assets retain their source paths and ordinary pattern, channel, byte, and multimodal semantics. Acquisition observes current source contents, including disappearance. |
5751
- | ```` ```runtime (skill://<name>/scripts/program.ext) ```` | Ordinary resource execution and proposal policy; preserve the native file and its siblings under {§exec-source-temporary}. Discovery and READ never execute scripts. |
5752
- | Model mutation | Read-only; no EDIT, SEND, or KILL of skill resources. Manage definitions and enablement through ```` ```skills ````. |
5753
- | Disable / unavailable / remove | Withdraw the authority from new resource access and discovery. Existing log receipts remain historical evidence. |
5754
- | WORK / FORK | Use the same workspace Functionality, not copied definitions or resource caches. |
5755
-
5756
- Explicit skill URIs address these resources; bare operation paths still address
5757
- project files, with no implicit current-skill directory. Source resolution follows
5758
- {§agent-skills-directory}, including symlinked skill directories and containment of references.
5759
- An uninstalled Git skill is not manufactured by repository detection.
5561
+ The skills module owns the Agent Skills family ({§skills-functionality}) and its resource trees
5562
+ ({§skills-resources}); core supplies the Plurnk reference through {§provided-skills-module-slice}.
5760
5563
 
5761
5564
  §plurnk-skill **Plurnk's own reference is an ordinary service-provided skill.**
5762
5565
  `skill://plurnk/SKILL.md` is the standard frontmatter entry point, catalogued with
5763
- other enabled skills. It links package-owned configuration, model, and COPY/MOVE chapters,
5764
- the complete `.env.defaults` aggregate at `skill://plurnk/.env.defaults`, and the
5765
- Worker's existing tool/resource references. No chapter or defaults body is
5766
- injected merely because the skill is enabled. The defaults bytes come from the
5566
+ other enabled skills. It links package-owned configuration, model, COPY/MOVE and extensibility
5567
+ chapters, the complete `.env.defaults` aggregate at `skill://plurnk/.env.defaults`, the
5568
+ installed contracts at `skill://plurnk/packages/<package name>/SPEC.md`, and the
5569
+ Worker's existing tool/resource references. The contracts are the service's own `SPEC.md` and
5570
+ each installed ecosystem package's that ships one, the same membership as the defaults
5571
+ aggregate, with that package's published type declarations beside it at
5572
+ `packages/<package name>/dist/**/*.d.ts`; the service's declarations are its internals and are not
5573
+ served. Contracts are listed when the tree is listed and read where each package installed them,
5574
+ never through a symbolic link. No chapter, contract or defaults body is injected merely because the
5575
+ skill is enabled. The defaults bytes come from the
5767
5576
  same {§operator-config-env-defaults} renderer as the operator command, never the
5768
5577
  effective environment. Native chapter files retain their owners and locations;
5769
5578
  runtime-generated bytes have no invented disk location. Disable/enable,
5770
5579
  shared workspace visibility, and project/global shadowing use the ordinary Skills lifecycle.
5771
- Service-provided skills are not installer targets; removal follows {§skills-remove}.
5772
-
5773
- §skills-remove **`remove` forgets the workspace binding, not the source.**
5774
- It withdraws that definition and restores any inherited definition and enabledness
5775
- ({§functionality-coordinator}). External folders are never deleted. Fetched
5776
- materializations remain workspace-owned operational state, reusable only for the
5777
- same complete definition; they confer no availability without a definition.
5778
- Inherited definitions cannot be removed here; their enabledness can be overridden.
5779
-
5780
- §skills-hotload **Skills placed out of band are admitted at the next turn** ({§functionality-hotload}). The
5781
- family keeps one signature of the discovered roots, configured definitions, source locations, and `SKILL.md` sources
5782
- per resident workspace, read before a publication loads the skills it describes. Turn admission
5783
- recomputes it under the workspace gate before packet assembly: a changed signature republishes the
5784
- family, and an unchanged one republishes only when the skills the coordinator would publish differ
5785
- from the published ones. A skill installed, edited or removed by any other tool is therefore
5786
- discoverable in the first subsequent model turn, while an unchanged set dispatches nothing. The model manages skills
5787
- only through the generated ```` ```skills ```` family
5788
- ({§functionality-model-projection}); it is never taught a package manager.
5580
+ Service-provided skills are host-provided trees, never fetched; removal follows {§skills-remove}.
5789
5581
 
5790
5582
  The catalog and Turn0 describe Functionality under the current workspace
5791
5583
  capability policy and service ceiling. A direct denied attempt receives the
@@ -5808,7 +5600,7 @@ section because they are language extensions rather than executable tools.
5808
5600
 
5809
5601
  ### §inject system.inject — the operator injection
5810
5602
 
5811
- §packet-inject When `PLURNK_SERVICE_PACKET_INJECT` names a readable markdown file, its content renders as an `## Operator Notes` section in the system slot (definition → policy → inject). Read per-turn so the operator's edits take effect live; a set-but-unreadable path fails the turn hard as {§policy-sections} rules. `~/` expands to home. It's the operator-side complement to the plugin section hook — a pressure valve so reshaping the packet edits operator content, never the core. Unset → no section.
5603
+ §packet-inject When `PLURNK_SERVICE_PACKET_INJECT` names a readable markdown file, its content renders as an `## Operator Notes` section in the system slot (definition → policy → inject). Read per-turn so the operator's edits take effect live; a set-but-unreadable path fails the turn hard as {§policy-sections} rules. `~/` expands to home. It's the operator-side complement to the extension packet transform — a pressure valve so reshaping the packet edits operator content, never the core. Unset → no section.
5812
5604
 
5813
5605
  ### §policy system.policy — the client's policy injection
5814
5606
 
@@ -5821,18 +5613,6 @@ surfaces with its cause and leaves no apparently initialized home.
5821
5613
  After that bootstrap the file is user-owned: edits and deletion persist, and a
5822
5614
  later boot never refreshes or recreates it.
5823
5615
 
5824
- §legacy-home-transition A legacy `~/.plurnk` is never an ambient fallback. If
5825
- legacy state exists while canonical destinations do not, ordinary startup
5826
- fails with the exact `plurnk-service paths migrate` recovery. That explicit,
5827
- idempotent command refuses destination conflicts and a live database owner,
5828
- moves known user configuration and durable SQLite files to their semantic
5829
- homes, byte-verifies the complete copied set before removing any source,
5830
- discards only recognized generated references, and removes the empty legacy
5831
- directory. A pre-commit failure rolls back canonical files and directories
5832
- created by that attempt. Unknown legacy members or simultaneous
5833
- legacy/canonical state fail without guessing. No dual read or dual write survives
5834
- the transition.
5835
-
5836
5616
  §schemes-self-doc-materialization **One generated reference tree per workspace.** `@plurnk/plurnk-schemes` owns `SchemeManifest.documentation` ({§manifest-self-doc}). Each reference carries an H2 `Summary` for ordinary catalog projection. `SchemeRegistry.docs(workspaceId)` resolves corpus-or-manifest documentation, and `referenceEntries` applies workspace capabilities to that source. One workspace materializer deletes vanished contributions and upserts current documents. Concurrent readers share the same reconciliation and outcome; independent workspaces do not serialize together. Ordinary runtime turns maintain {§worker-generated-subtree}, including under read-only workspace policy.
5837
5617
 
5838
5618
  ### §packet-git-status The Git status section — compact repository state
@@ -6201,12 +5981,12 @@ caller's own run directory.
6201
5981
 
6202
5982
  Every Problem code core mints is named here under its family ({§problem-error-carrier} carries it); the root lint (`scripts/problem-codes.mjs`) refuses a code no owning SPEC names.
6203
5983
 
6204
- §problems-dispatch **Dispatch Problems.** Every code this family mints, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
5984
+ §problems-dispatch **Dispatch Problems.** Every code minted here, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6205
5985
 
6206
5986
  | code | status | contract |
6207
5987
  |---|---:|---|
6208
5988
  | `target-required` | 400 | *OP* requires a target path. Recovery: Write the target in parentheses on the opening fence line: `OP (path)`. |
6209
- | `scheme-not-found` | 501 | Scheme '*name*' is not registered. Recovery: ({§unregistered-scheme-recovery}) `` `*host*` is an executor: run ```*host* (*tool*)``` with its input in the body. `` when the address's authority is one, else `Registered schemes: *names*.` |
5989
+ | `scheme-not-found` | 501 | Scheme '*name*' is not registered. Recovery: ({§unregistered-scheme-recovery}) `Registered schemes: *names*.` |
6210
5990
  | `scheme-metadata-unsupported` | 400 | *OP* on '*scheme*' does not accept the [metadata] modifier. |
6211
5991
  | `operation-not-implemented` | 501 | Scheme '*name*' does not implement *OP* (or exec). |
6212
5992
  | `entry-read-not-implemented` | 501 | The '*scheme*' scheme does not provide entry reads. |
@@ -6229,17 +6009,15 @@ Every Problem code core mints is named here under its family ({§problem-error-c
6229
6009
  | `channel-required` | 400 | The '*scheme*' scheme has no default channel. Recovery: Address a named channel with a URI fragment. |
6230
6010
  | `binary-source-unsupported` | 415 | Channel #*name* is binary and its scheme keeps no bytes to transfer. |
6231
6011
  | `metadata-unsupported` | 400 | *OP* takes only the env option; '*key*' is not one. |
6232
- | `worker-name-conflict` | 409 | Worker '*name*' already exists in this workspace. Recovery: To give '*name*' more work, write `SEND (worker://_name_)` with the task as the body; to start another worker, choose a name no worker holds. |
6012
+ | `worker-name-conflict` | 409 | Worker '*name*' already exists in this workspace. |
6233
6013
  | `no-operation` | 422 | The turn performed no operation ({§empty-turn}). |
6234
6014
  | `send-target-not-a-recipient` | 400 | The addressed scheme is not a SEND recipient. Recovery: A targetless SEND answers the open messages; a directed SEND requires a recipient that implements SEND. |
6235
6015
 
6236
- §unregistered-scheme-recovery **An unregistered scheme's refusal names what does exist.** When the
6237
- address's authority is an executor available to the workspace — an MCP server's tool written as
6238
- `tool://gh/get_me` — the recovery is that executor's fence with the address's path as its target;
6239
- otherwise it lists the schemes the workspace registers. READ, FIND, KILL and resource selection share
6240
- the sentence.
6016
+ §unregistered-scheme-recovery **An unregistered scheme's refusal names what does exist.** Its recovery
6017
+ lists the schemes the workspace registers, and rebuilds the address into nothing else
6018
+ ({§diagnostic-observation}). READ, FIND, KILL and resource selection share the sentence.
6241
6019
 
6242
- §problems-content **Content and transfer Problems.** Every code this family mints, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6020
+ §problems-content **Content and transfer Problems.** Every code minted here, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6243
6021
 
6244
6022
  | code | status | contract |
6245
6023
  |---|---:|---|
@@ -6256,7 +6034,7 @@ the sentence.
6256
6034
  | `proposal-apply-missing` | 500 | The source scheme accepted its MOVE proposal without applying the source mutation. |
6257
6035
  | `line-anchor-collision` | 409 | READ coordinates collided with current content at *target*. |
6258
6036
 
6259
- §problems-file **File scheme Problems.** Every code this family mints, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6037
+ §problems-file **File scheme Problems.** Every code minted here, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6260
6038
 
6261
6039
  | code | status | contract |
6262
6040
  |---|---:|---|
@@ -6269,13 +6047,13 @@ the sentence.
6269
6047
  | `path-names-no-file` | 403 | The spelling '*path*' does not name a file: it is empty, or it names a directory. |
6270
6048
  | `path-occupied-by-nonmember` | 403 | A non-member file already occupies '*path*'. Recovery: Choose an unoccupied member path. |
6271
6049
  | `path-outside-workspace` | 403 | A symlink on '*path*' resolves outside the namespace. |
6272
- | `binary-write-unsupported` | 415 | A text EDIT cannot author binary '*mimetype*'; COPY or MOVE the bytes instead. |
6050
+ | `binary-write-unsupported` | 415 | A text EDIT cannot author binary '*mimetype*'. |
6273
6051
  | `file-create-excluded` | 403 | A members exclusion (`!_glob_`) covers '*path*'. Recovery: Remove or disable the excluding members definition, or choose another path. |
6274
6052
  | `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. |
6275
6053
  | `file-materialization-limit` | 413 | The file exceeds the materialization byte limit and is not read into the workspace. |
6276
- | `entry-not-found` | 404 | No member of this workspace is at '*path*'. Recovery: Check the path with FIND. 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, so keep working files inside the root. |
6054
+ | `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. |
6277
6055
 
6278
- §problems-exec **Execution Problems.** Every code this family mints, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6056
+ §problems-exec **Execution Problems.** Every code minted here, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6279
6057
 
6280
6058
  | code | status | contract |
6281
6059
  |---|---:|---|
@@ -6284,7 +6062,7 @@ the sentence.
6284
6062
  | `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*`. |
6285
6063
  | `input-closed` | 410 | Execution input is closed. |
6286
6064
 
6287
- §problems-entries **Entry scheme Problems (log, worker, entries).** Every code this family mints, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6065
+ §problems-entries **Entry scheme Problems (log, worker, entries).** Every code minted here, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6288
6066
 
6289
6067
  | code | status | contract |
6290
6068
  |---|---:|---|
@@ -6297,33 +6075,20 @@ the sentence.
6297
6075
  | `message-empty` | 400 | SEND has no message text or attachments. |
6298
6076
  | `scope-unsupported` | 400 | A worker SEND takes no scope. |
6299
6077
 
6300
- §problems-functionality **Server and Functionality Problems.** Every code this family mints, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6078
+ §problems-functionality **Server and Functionality Problems.** Every code minted here, its status, and the sentence that is its contract (placeholders in *italics* are filled at emission; a fixed recovery follows its detail).
6301
6079
 
6302
6080
  | code | status | contract |
6303
6081
  |---|---:|---|
6304
6082
  | `service-starting` | 503 | The PLURNK service owns this listener but has not admitted its client interface yet. |
6305
- | `configuration-unsupported` | 400 | Environment discovery reads this installation's declared configuration; client configuration contributes nothing. |
6083
+ | `route-not-found` | 404 | The requested HTTP route does not exist. |
6306
6084
  | `configuration-invalid` | 503 | The owning configuration reader's diagnostic, naming the invalid key. Recovery: Correct the named configuration input. Other capabilities remain available. |
6307
6085
  | `name-reserved` | 400 | '*alias*' is plurnk's own: PLURNK_* configuration and provider credential names never reach a subprocess. |
6308
6086
  | `value-invalid` | 400 | '*alias*' needs a string value. |
6309
6087
  | `env-invalid` | 400 | `env` must be an object of string values; '*name*' is not a name a shell can export. |
6310
- | `query-required` | 400 | discover takes a path or a glob. Recovery: Supply `{ "query": "<path or glob>" }`. |
6088
+ | `query-invalid` | 400 | '*query*' names no path or pattern. Recovery: Supply a path such as `docs/guide.md` or a pattern such as `docs/**`. |
6311
6089
  | `headless` | 409 | The workspace has no project root, so there are no file members. Recovery: Open the workspace on a project root. |
6312
- | `definition-invalid` | 400 | A members definition is { glob }: a gitignore-style pattern, `!glob` to exclude; a skill definition names an installable skill. |
6090
+ | `definition-invalid` | 400 | A members definition is { glob }: a gitignore-style pattern, `!glob` to exclude. |
6313
6091
  | `model-scope` | 403 | The model may not change membership here: the members scope is none. Recovery: `git add` the file so git tracks it, or ask the operator to add it (/members add) or raise PLURNK_SERVICE_MEMBERS_MODEL_SCOPE. |
6314
- | `query-unsupported` | 400 | Agent Skills have no standard registry to search; discover takes a source: a git remote as a full https or ssh URL, a folder, a lone SKILL.md, or a zip or tar archive. |
6315
- | `source-invalid` | 400 | '*source*' is not a valid git remote URL; an https source carries no credentials (git's credential helper supplies them); is not a source: a git remote is a full https or ssh URL; is relative, and this workspace has no project root to resolve it against; or is neither a folder, a SKILL.md, nor a zip or tar archive. |
6316
- | `source-missing` | 404 | No folder or file is at '*source*' (; owner/repo shorthand names no forge, so give the repository's full https or ssh URL). |
6317
- | `source-unreadable` | 422 | '*source*' cannot be read: *cause*; or '*path*' could not be unpacked: *reason*. |
6318
- | `source-unreachable` | 502 | git could not reach '*remote*', or fetch it (at '*ref*'): *reason*. |
6319
- | `ref-missing` | 404 | '*remote*' has no branch or tag '*ref*', or names no default branch. |
6320
- | `source-moved` | 409 | '*source*' *ref* now names *current*; this skill was added at *commit*. Recovery: Remove the skill and add it again to take the current commit. |
6321
- | `source-is-plugin` | 422 | '*source*' is an Agent Plugin; install it as a plugin, so its skills keep the plugin's identity and servers. |
6322
- | `source-unsafe` | 422 | '*path*' links outside its skill, or is neither a file, a directory, nor an inward link. |
6323
- | `skill-not-found` | 404 | '*source*' carries no Agent Skill named '*name*'. |
6324
- | `skill-ambiguous` | 409 | '*source*' carries *count* skills named '*name*'. |
6325
- | `alias-mismatch` | 400 | Alias '*alias*' must equal the skill name '*name*'. |
6326
- | `source-required` | 400 | Adding '*alias*' requires the source that provides it. |
6327
6092
  | `workspace-not-found` | 404 | Workspace *id* does not exist. |
6328
6093
  | `state-not-json` | 400 | Worker module state is not JSON-serializable. |
6329
6094
  | `workspace-busy` | 409 | Workspace *id* is running an operation or another capability change. Recovery: Settle the current operation and retry the capability change. |
@@ -6333,12 +6098,8 @@ the sentence.
6333
6098
  | `offset-channel-required` | 400 | Recovery: Select the channel to read from the offset. |
6334
6099
  | `target-invalid` | 400 | Recovery: Use a scheme://path target. |
6335
6100
  | `proposal-not-pending` | 409 | Recovery: Refresh pending proposals before resolving one. |
6336
- | `loop-policy-invalid` | 400 | An unattended loop cannot hold a proposal for review: nobody is present to answer. Recovery: State proposals accept or reject, or attend the loop. |
6337
6101
  | `scope-cancelled` | 499 | The worker scope was cancelled: *reason*. |
6338
- | `range-not-satisfiable` | 416 | `Range <0,-1>` starts at 0, which is not a line; lines are numbered from 1. Recovery: Write `<1,-1>` to trim every line of the body; `KILL (log:///…/READ)` with no scope retires the item. |
6339
- | `install-failed` | 500 | Agent Skill '*name*' could not be placed under *root*: *cause*. |
6340
- | `skill-missing` | 404 | Agent Skill '*alias*' is not provided by this service. |
6341
- | `skill-invalid` | 422 | Agent Skill '*alias*' is not a valid standard skill: *cause*. |
6102
+ | `range-not-satisfiable` | 416 | `Range <0,-1>` starts at 0, which is not a line; lines are numbered from 1. Recovery: Trim one line with `<L>` or lines L through M with `<L,M>`; `KILL (log:///…/READ)` with no scope retires the whole row. |
6342
6103
 
6343
6104
  §pinned-wording-core **Pinned wording.** Verbatim sentences tests pin: each is contract, and a change here is a change of contract.
6344
6105
 
@@ -6348,12 +6109,13 @@ the sentence.
6348
6109
  | Completion deferred. Conclude with KILL alone. | a concluding KILL that carries other operations ({§kill-conclusion}) |
6349
6110
  | Context exceeds budget. YOU MUST ONLY KILL, MOVE or NOTE this turn. | the over-budget row ({§context-over-budget-row}) |
6350
6111
  | Context window overflow: the packet cannot fit the model's window even as receipts. | the wall ({§context-wall}) |
6351
- | This run is unattended: nobody is present to answer. | a capability that needs a present operator in an unattended loop ({§loop-attendance}) |
6112
+ | No recipient implements the requested interaction. | The worker's owner is not interactive or does not declare the required client tool ({§client-interaction-routing}). |
6113
+ | 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}) |
6352
6114
  | 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 |
6353
6115
  | 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 |
6354
6116
  | The stream was cancelled by KILL. | a stream terminal after KILL |
6355
6117
  | '*program*' exited with code *n*. | an execution's non-zero exit |
6356
- | '*path*' is a directory, not a file; READ reads one file. Recovery: List its files with `FIND (_path_/)`, then READ one by its path. | READ of a directory |
6118
+ | '*path*' is a directory, not a file; READ reads one file. Recovery: `FIND (_path_/)` lists its files. | READ of a directory |
6357
6119
  | The execution at ops://*worker*/*loop* has not concluded. | a bare READ of a running worker's result (425) |
6358
6120
  | The child provider failed. | a child's provider failure read back by its parent |
6359
6121
  | '*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 |