@plurnk/plurnk-service 1.26.0 → 1.27.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 (227) hide show
  1. package/.env.defaults +3 -6
  2. package/SPEC.md +260 -126
  3. package/dist/build-info.json +1 -1
  4. package/dist/content/body-preview.d.ts +1 -0
  5. package/dist/content/body-preview.d.ts.map +1 -1
  6. package/dist/content/body-preview.js +7 -0
  7. package/dist/content/body-preview.js.map +1 -1
  8. package/dist/content/byte-view.d.ts +19 -2
  9. package/dist/content/byte-view.d.ts.map +1 -1
  10. package/dist/content/byte-view.js +67 -14
  11. package/dist/content/byte-view.js.map +1 -1
  12. package/dist/content/edit-receipt.js +1 -1
  13. package/dist/content/edit-receipt.js.map +1 -1
  14. package/dist/content/line-marker.js +1 -1
  15. package/dist/content/line-marker.js.map +1 -1
  16. package/dist/content/line-selection.d.ts +3 -1
  17. package/dist/content/line-selection.d.ts.map +1 -1
  18. package/dist/content/line-selection.js +27 -3
  19. package/dist/content/line-selection.js.map +1 -1
  20. package/dist/content/matcher.d.ts +9 -14
  21. package/dist/content/matcher.d.ts.map +1 -1
  22. package/dist/content/matcher.js +51 -45
  23. package/dist/content/matcher.js.map +1 -1
  24. package/dist/content/pattern-edits.d.ts +7 -26
  25. package/dist/content/pattern-edits.d.ts.map +1 -1
  26. package/dist/content/pattern-edits.js +54 -91
  27. package/dist/content/pattern-edits.js.map +1 -1
  28. package/dist/content/read-projector.d.ts +2 -1
  29. package/dist/content/read-projector.d.ts.map +1 -1
  30. package/dist/content/read-projector.js +39 -7
  31. package/dist/content/read-projector.js.map +1 -1
  32. package/dist/content/read-resolve.js +1 -1
  33. package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
  34. package/dist/core/AdmittedTurnExecutor.js +3 -3
  35. package/dist/core/AdmittedTurnExecutor.js.map +1 -1
  36. package/dist/core/ChannelWrite.d.ts +2 -3
  37. package/dist/core/ChannelWrite.d.ts.map +1 -1
  38. package/dist/core/ChannelWrite.js +21 -22
  39. package/dist/core/ChannelWrite.js.map +1 -1
  40. package/dist/core/ChannelWrite.sql +26 -0
  41. package/dist/core/DataStatementRunner.d.ts.map +1 -1
  42. package/dist/core/DataStatementRunner.js +1 -0
  43. package/dist/core/DataStatementRunner.js.map +1 -1
  44. package/dist/core/Dispatcher.d.ts +2 -2
  45. package/dist/core/Dispatcher.d.ts.map +1 -1
  46. package/dist/core/Dispatcher.js +6 -13
  47. package/dist/core/Dispatcher.js.map +1 -1
  48. package/dist/core/EditMutations.d.ts +5 -5
  49. package/dist/core/EditMutations.d.ts.map +1 -1
  50. package/dist/core/EditMutations.js +44 -50
  51. package/dist/core/EditMutations.js.map +1 -1
  52. package/dist/core/EditSequence.js +2 -2
  53. package/dist/core/EditSequence.js.map +1 -1
  54. package/dist/core/Engine.d.ts +1 -0
  55. package/dist/core/Engine.d.ts.map +1 -1
  56. package/dist/core/Engine.js +3 -0
  57. package/dist/core/Engine.js.map +1 -1
  58. package/dist/core/InferenceCall.js +2 -2
  59. package/dist/core/InferenceCall.js.map +1 -1
  60. package/dist/core/InferenceCall.sql +1 -0
  61. package/dist/core/LiveSubscription.d.ts +20 -0
  62. package/dist/core/LiveSubscription.d.ts.map +1 -0
  63. package/dist/core/LiveSubscription.js +68 -0
  64. package/dist/core/LiveSubscription.js.map +1 -0
  65. package/dist/core/LiveSubscriptions.d.ts +1 -0
  66. package/dist/core/LiveSubscriptions.d.ts.map +1 -1
  67. package/dist/core/LiveSubscriptions.js +8 -0
  68. package/dist/core/LiveSubscriptions.js.map +1 -1
  69. package/dist/core/LogBody.js +1 -1
  70. package/dist/core/LogBody.js.map +1 -1
  71. package/dist/core/LoopDriver.js +2 -1
  72. package/dist/core/LoopDriver.js.map +1 -1
  73. package/dist/core/LoopLifecycle.d.ts +2 -2
  74. package/dist/core/LoopLifecycle.d.ts.map +1 -1
  75. package/dist/core/LoopLifecycle.js +2 -4
  76. package/dist/core/LoopLifecycle.js.map +1 -1
  77. package/dist/core/LoopLifecycle.sql +1 -6
  78. package/dist/core/MutationEffects.d.ts +1 -3
  79. package/dist/core/MutationEffects.d.ts.map +1 -1
  80. package/dist/core/MutationEffects.js +2 -1
  81. package/dist/core/MutationEffects.js.map +1 -1
  82. package/dist/core/PacketBuilder.d.ts.map +1 -1
  83. package/dist/core/PacketBuilder.js +10 -3
  84. package/dist/core/PacketBuilder.js.map +1 -1
  85. package/dist/core/PacketBuilder.sql +4 -5
  86. package/dist/core/PatternSelection.d.ts +5 -10
  87. package/dist/core/PatternSelection.d.ts.map +1 -1
  88. package/dist/core/PatternSelection.js +9 -16
  89. package/dist/core/PatternSelection.js.map +1 -1
  90. package/dist/core/ResourceMutations.d.ts +2 -4
  91. package/dist/core/ResourceMutations.d.ts.map +1 -1
  92. package/dist/core/ResourceMutations.js +2 -5
  93. package/dist/core/ResourceMutations.js.map +1 -1
  94. package/dist/core/ResourceSelector.d.ts +2 -2
  95. package/dist/core/ResourceSelector.d.ts.map +1 -1
  96. package/dist/core/ResourceSelector.js +64 -33
  97. package/dist/core/ResourceSelector.js.map +1 -1
  98. package/dist/core/ResourceTransfers.d.ts +2 -4
  99. package/dist/core/ResourceTransfers.d.ts.map +1 -1
  100. package/dist/core/ResourceTransfers.js +74 -50
  101. package/dist/core/ResourceTransfers.js.map +1 -1
  102. package/dist/core/SchemeRegistry.d.ts +1 -0
  103. package/dist/core/SchemeRegistry.d.ts.map +1 -1
  104. package/dist/core/SchemeRegistry.js +11 -9
  105. package/dist/core/SchemeRegistry.js.map +1 -1
  106. package/dist/core/ServiceTeardown.d.ts +3 -2
  107. package/dist/core/ServiceTeardown.d.ts.map +1 -1
  108. package/dist/core/ServiceTeardown.js +17 -8
  109. package/dist/core/ServiceTeardown.js.map +1 -1
  110. package/dist/core/StopDeadline.d.ts +5 -0
  111. package/dist/core/StopDeadline.d.ts.map +1 -0
  112. package/dist/core/StopDeadline.js +20 -0
  113. package/dist/core/StopDeadline.js.map +1 -0
  114. package/dist/core/ToolResources.d.ts.map +1 -1
  115. package/dist/core/ToolResources.js +11 -2
  116. package/dist/core/ToolResources.js.map +1 -1
  117. package/dist/core/TurnDispositionHandler.d.ts +4 -2
  118. package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
  119. package/dist/core/TurnDispositionHandler.js +24 -6
  120. package/dist/core/TurnDispositionHandler.js.map +1 -1
  121. package/dist/core/TurnRunner.d.ts.map +1 -1
  122. package/dist/core/TurnRunner.js +14 -21
  123. package/dist/core/TurnRunner.js.map +1 -1
  124. package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
  125. package/dist/core/caps/DbSubscriptionCaps.js +13 -19
  126. package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
  127. package/dist/core/content-hash.d.ts +1 -1
  128. package/dist/core/content-hash.d.ts.map +1 -1
  129. package/dist/core/content-hash.js.map +1 -1
  130. package/dist/core/mutation-types.d.ts +6 -4
  131. package/dist/core/mutation-types.d.ts.map +1 -1
  132. package/dist/core/packet-wire.d.ts.map +1 -1
  133. package/dist/core/packet-wire.js +30 -1
  134. package/dist/core/packet-wire.js.map +1 -1
  135. package/dist/core/provider-accounting.d.ts +1 -1
  136. package/dist/core/provider-accounting.d.ts.map +1 -1
  137. package/dist/core/provider-accounting.js +2 -1
  138. package/dist/core/provider-accounting.js.map +1 -1
  139. package/dist/digest/DigestEvidence.d.ts +1 -0
  140. package/dist/digest/DigestEvidence.d.ts.map +1 -1
  141. package/dist/digest/DigestEvidence.js +6 -0
  142. package/dist/digest/DigestEvidence.js.map +1 -1
  143. package/dist/digest/DigestRender.d.ts +1 -0
  144. package/dist/digest/DigestRender.d.ts.map +1 -1
  145. package/dist/digest/DigestRender.js +33 -11
  146. package/dist/digest/DigestRender.js.map +1 -1
  147. package/dist/digest/DigestRequiem.js +4 -4
  148. package/dist/digest/DigestRequiem.js.map +1 -1
  149. package/dist/digest/digest-rows.d.ts +1 -0
  150. package/dist/digest/digest-rows.d.ts.map +1 -1
  151. package/dist/digest/digest.sql +5 -0
  152. package/dist/schemes/Exec.d.ts.map +1 -1
  153. package/dist/schemes/Exec.js +28 -27
  154. package/dist/schemes/Exec.js.map +1 -1
  155. package/dist/schemes/File.d.ts.map +1 -1
  156. package/dist/schemes/File.js +1 -2
  157. package/dist/schemes/File.js.map +1 -1
  158. package/dist/schemes/Log.d.ts.map +1 -1
  159. package/dist/schemes/Log.js +4 -17
  160. package/dist/schemes/Log.js.map +1 -1
  161. package/dist/schemes/TurnSource.js +2 -2
  162. package/dist/schemes/TurnSource.js.map +1 -1
  163. package/dist/schemes/_derivation-use.d.ts +7 -0
  164. package/dist/schemes/_derivation-use.d.ts.map +1 -0
  165. package/dist/schemes/_derivation-use.js +40 -0
  166. package/dist/schemes/_derivation-use.js.map +1 -0
  167. package/dist/schemes/_entry-crud.d.ts +6 -1
  168. package/dist/schemes/_entry-crud.d.ts.map +1 -1
  169. package/dist/schemes/_entry-crud.js.map +1 -1
  170. package/dist/schemes/_entry-find.d.ts.map +1 -1
  171. package/dist/schemes/_entry-find.js +13 -49
  172. package/dist/schemes/_entry-find.js.map +1 -1
  173. package/dist/schemes/_entry-find.sql +0 -11
  174. package/dist/schemes/_entry-fts.d.ts.map +1 -1
  175. package/dist/schemes/_entry-fts.js +9 -3
  176. package/dist/schemes/_entry-fts.js.map +1 -1
  177. package/dist/schemes/_entry-fts.sql +14 -8
  178. package/dist/schemes/_entry-graph.d.ts +5 -8
  179. package/dist/schemes/_entry-graph.d.ts.map +1 -1
  180. package/dist/schemes/_entry-graph.js +33 -72
  181. package/dist/schemes/_entry-graph.js.map +1 -1
  182. package/dist/schemes/_entry-graph.sql +30 -48
  183. package/dist/schemes/_entry-manifest.d.ts +5 -0
  184. package/dist/schemes/_entry-manifest.d.ts.map +1 -1
  185. package/dist/schemes/_entry-manifest.js +5 -0
  186. package/dist/schemes/_entry-manifest.js.map +1 -1
  187. package/dist/schemes/_entry-ops.d.ts +1 -1
  188. package/dist/schemes/_entry-ops.d.ts.map +1 -1
  189. package/dist/schemes/_entry-ops.js +4 -11
  190. package/dist/schemes/_entry-ops.js.map +1 -1
  191. package/dist/schemes/_search-index.d.ts +10 -0
  192. package/dist/schemes/_search-index.d.ts.map +1 -1
  193. package/dist/schemes/_search-index.js +33 -1
  194. package/dist/schemes/_search-index.js.map +1 -1
  195. package/dist/schemes/exec-lifetime.d.ts.map +1 -1
  196. package/dist/schemes/exec-lifetime.js +1 -2
  197. package/dist/schemes/exec-lifetime.js.map +1 -1
  198. package/dist/server/Daemon.d.ts +2 -1
  199. package/dist/server/Daemon.d.ts.map +1 -1
  200. package/dist/server/Daemon.js +20 -33
  201. package/dist/server/Daemon.js.map +1 -1
  202. package/dist/server/DrainSupervisor.d.ts +2 -1
  203. package/dist/server/DrainSupervisor.d.ts.map +1 -1
  204. package/dist/server/DrainSupervisor.js +37 -56
  205. package/dist/server/DrainSupervisor.js.map +1 -1
  206. package/dist/server/Functionality.d.ts +1 -1
  207. package/dist/server/Functionality.d.ts.map +1 -1
  208. package/dist/server/Functionality.js +24 -11
  209. package/dist/server/Functionality.js.map +1 -1
  210. package/dist/server/FunctionalityManager.js +2 -2
  211. package/dist/server/FunctionalityManager.js.map +1 -1
  212. package/dist/server/Retention.d.ts.map +1 -1
  213. package/dist/server/Retention.js +7 -3
  214. package/dist/server/Retention.js.map +1 -1
  215. package/dist/server/drain.sql +0 -6
  216. package/dist/server/seam-loop.sql +1 -1
  217. package/dist/service.js +1 -1
  218. package/dist/service.js.map +1 -1
  219. package/docs/env.md +4 -3
  220. package/migrations/013_graph_regions.sql +31 -0
  221. package/migrations/014_stream_timing.sql +3 -0
  222. package/migrations/015_request_evidence.sql +3 -0
  223. package/package.json +34 -34
  224. package/dist/server/exec-poll-backoff.d.ts +0 -2
  225. package/dist/server/exec-poll-backoff.d.ts.map +0 -1
  226. package/dist/server/exec-poll-backoff.js +0 -6
  227. package/dist/server/exec-poll-backoff.js.map +0 -1
package/SPEC.md CHANGED
@@ -143,7 +143,7 @@ which depends on core semantic conventions v1.44.0: a CLIENT-kind
143
143
  spelling, never its tuning alias; unmatched IDs are preserved, `other` for an
144
144
  unregistered handle), and `gen_ai.request.model`;
145
145
  on settlement it gains `gen_ai.usage.input_tokens` and
146
- `gen_ai.usage.output_tokens` by aggregating every reported request quantity in
146
+ `gen_ai.usage.output_tokens` for quantities reported by every physical request in
147
147
  validated accounting ({§tokenomics-provider-usage}), plus
148
148
  `gen_ai.response.finish_reasons`; failures carry `error.type` as the class
149
149
  name only. Plurnk custom attributes (attempt, kind, status, loop/turn ids)
@@ -586,9 +586,17 @@ Every admitted authority is a literal `workers.name`; self-addressing uses the c
586
586
  the errors section), just above it. Body-suppressed child activity is durable
587
587
  history; this section is the current inventory that keeps an active obligation
588
588
  visible even when no new activity arrived. Each open stream pointer carries
589
- its channels' sizes and growth since the last packet in `detail`
590
- (`{"status":"active","path":"sh:///ab3d5678","detail":"stdout 340 lines (+2048 bytes)"}`)
591
- — the only thing the model learns about a stream before it closes
589
+ elapsed runtime, output inactivity, and its channels' sizes and growth since
590
+ the last packet in `detail`
591
+ (`{"status":"active","path":"sh:///ab3d5678","detail":"elapsed 300s; output unchanged 120s; stdout 340 lines (+2048 bytes)"}`).
592
+ Both durations are whole seconds at packet assembly, floored and bounded
593
+ below by zero. Runtime starts at subscription opening. Output inactivity
594
+ starts there too, then resets atomically when any published channel's content
595
+ changes, including replacement or clearing. Identical writes, empty appends,
596
+ READs, publication acknowledgements, metadata, and channel-state changes do
597
+ not reset it. The clock is durable; an upgraded stream lacking that evidence
598
+ reports `output timing unknown` until its next content change, never a guessed
599
+ timestamp. These are observations, not new timeouts or wake conditions
592
600
  ({§exec-stream}). It is orienting state, never advice: the model sees its live
593
601
  subtree (`{"status":102,"path":"worker://worker-x"}`) and reasons for itself —
594
602
  READ, SEND, or KILL via the path.
@@ -736,7 +744,8 @@ identity, and terminal disposition without exposing raw bytes or a base64 lane.
736
744
  | ---------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------- |
737
745
  | Text | Verbatim Unicode under the detected textual mimetype | READ and EDIT use the snapshot. |
738
746
  | Binary with readable projection | Derived Unicode as `text/markdown` | READ uses the projection; source-aware EDIT remains 415. |
739
- | Binary without projection/over cap | Empty marker under the source binary mimetype | READ and EDIT return 415; private metadata distinguishes unavailable from limit. |
747
+ | Binary without projection | Empty marker under the source binary mimetype | READ uses source bytes under {§read-bytes}; text EDIT remains 415. |
748
+ | Failed or oversized acquisition | Empty marker carrying the producer failure | The operation preserves that failure under {§membership-materialization-limit}. |
740
749
 
741
750
  §membership-materialization-limit **A pathological member degrades, never the
742
751
  workspace.** `PLURNK_SERVICE_FILE_MATERIALIZE_MAX_BYTES` is a required positive
@@ -836,19 +845,38 @@ all use that one definition.
836
845
 
837
846
  ### §worker-wait-timing Durable waits and wake ownership
838
847
 
839
- WAIT has no timing operand; its optional path is a label ({§send-wait-scope}).
840
- With live work—an open stream or a live child worker—the loop parks durably
841
- and wakes on settlement, on a
842
- message, or on the inherited observation cadence of its open streams
843
- ({§exec-lifetime}); without live work it continues at once, told so. A wake
844
- continues the same loop with the same messages, generation policy, and
845
- cumulative turn ceiling; it never creates another assignment. Scheduled messages
846
- exist independently of a loop's optional attachment to one occurrence.
848
+ With live work—an open stream or a live child worker—the loop parks until an
849
+ ordinary wake or its maximum wait elapses. `PLURNK_SERVICE_WAIT_SEC` supplies the
850
+ bound; `WAIT <seconds>` overrides that one park ({§send-wait-scope}). The wake time
851
+ commits atomically with the wait revision, rounded up to the next millisecond.
852
+ Expiry queues the same loop through normal worker/provider admission; it never
853
+ cancels work or fabricates a result.
854
+
855
+ With live work, each WAIT receipt exposes `waitSeconds`, the maximum accepted
856
+ for that operation. It is not elapsed time or a promised sleep: an ordinary wake
857
+ or a shorter sibling WAIT can resume the loop sooner. An idle WAIT has no bound.
858
+ Historical unbounded receipts do not acquire one during projection.
859
+
860
+ | Boundary | Outcome |
861
+ |---|---|
862
+ | Message or work settlement before expiry | Wake normally; retire the old timer. |
863
+ | Several WAITs | One park at the earliest requested bound; a bare WAIT requests the configured bound. |
864
+ | Zero bound | Continue at 102 without parking, a timer, cancellation, or a warning; a later WAIT chooses its own bound. |
865
+ | Eligible completion joining live work | Use the configured bound. |
866
+ | No live work | Continue immediately; do not arm a timer. |
867
+ | Later WAIT after any wake | New wait identity and bound; no inherited override or backoff. |
868
+ | Restart | Restore the durable due time; an overdue wait becomes runnable. |
869
+ | Cancellation or terminal loop | Late timers are inert. |
870
+ | Provider-recovery or client-review wait | Keep its owning recovery/admission semantics, not this observation bound. |
871
+
872
+ A wake preserves the loop's messages, generation policy, cumulative turn ceiling,
873
+ and execution allowance. Parked time does not consume that allowance. Scheduled
874
+ messages remain independent; WAIT neither creates one nor selects its waker.
847
875
 
848
876
  ```mermaid
849
877
  stateDiagram-v2
850
878
  Running --> Parked: atomically persist the wait identity
851
- Parked --> Queued: arrival / completion / inherited observation, guarded by wait identity
879
+ Parked --> Queued: arrival / completion / wait expiry, guarded by wait identity
852
880
  Parked --> Terminal: cancellation
853
881
  Queued --> Running: same loop claimed by its worker's drain
854
882
  ```
@@ -964,7 +992,7 @@ completion. Closure is always a wake edge.
964
992
 
965
993
  | §worker-lifecycle-poll-matrix stream | While open | On closure |
966
994
  |------------------------------------------------|---|---|
967
- | any lifetime but `turn` | the daemon's exponential-backoff observation wakes ({§exec-lifetime}) | resume once with terminal observation |
995
+ | any lifetime but `turn` | bounded wait expiry permits inspection ({§worker-wait-timing}) | resume once with terminal observation |
968
996
  | `[{"lifetime":"turn"}]` | reap at the next pre-turn boundary | surface the terminal outcome |
969
997
 
970
998
  The structured-concurrency sequence is identical whether a child performs an
@@ -980,7 +1008,7 @@ sequenceDiagram
980
1008
  P->>P: WAIT parks on live child
981
1009
  C->>S: execution opens subscription
982
1010
  C->>C: WAIT parks on live stream
983
- loop backoff, fixed cadence, or explicit arrival
1011
+ loop wait expiry or explicit arrival
984
1012
  S-->>C: optional progress observation
985
1013
  C->>C: continue or park
986
1014
  end
@@ -1294,7 +1322,6 @@ The parser owns its boundaries; core admits determinate work and exposes its fai
1294
1322
  | Lost boundary after a closed operation | Admit the closed operations and record the boundary diagnostic under {§unparsed-tail-boundary}. |
1295
1323
  | Lost boundary before any closed operation | Reject the attempt; neither outside text nor a reasoning NOTE substitutes for a closed operational statement. Reasoning FIND/READ count as such statements ({§reasoning-operations}). |
1296
1324
  | Outside text carrying a log-entry heading, other than an emission row's | Reject the attempt ({§fabricated-log-entry}). |
1297
- | A response the provider stopped at a repeated line | Reject the attempt with the provider's sentence as its diagnostic ({§repetition-stop}); no provider recovery, notice, or problem row. |
1298
1325
 
1299
1326
  §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.
1300
1327
 
@@ -1613,7 +1640,7 @@ Registration precedes loop affinity:
1613
1640
  - §op-synchronous **Decisive operations settle before the next operation.** The dispatcher awaits each operation and its proposal resolution. Work remains in flight only when the operation's contract deliberately creates concurrency: FORK, WORK, a stream-producing execution, and streaming READ after acquisition. Such a READ first establishes its durable subscription and returns `102`; a later operation may address that live owner. Dispatching an execution before KILL does not wait for the process to finish using a resource. KILL of a worker synchronously ends its live loops before disposition checks the pending set; physical scope cleanup remains asynchronous.
1614
1641
  - §edit-execution **One authored EDIT is one mutation.** Each EDIT resolves against current resource state when dispatch reaches it, owns its proposal when gated, and records its own resulting revision. No later EDIT is prepared or applied in advance. Numeric scopes address current coordinates; an earlier EDIT may change what those numbers select. Rejection applies only to that operation, not its successful siblings.
1615
1642
  - §edit-anchor-continuity **Own EDITs preserve untouched hash targets within one program.** Core carries an anchor through exact, successfully applied EDIT splices when its line survives unchanged, even if its ordinal or neighborhood changes. Scoped entry KILL uses the same deletion path. Target-line replacement or deletion invalidates that binding. Continuity is private to the admitted program and canonical resource/channel; it is not a new published anchor format. The complete normalized line content must match the expected result of the preceding recorded EDIT, otherwise retained bindings are discarded and ordinary current-state validation applies. Reviewer replacement and results without an applied EDIT receipt do not carry bindings forward. A retained binding resolves alone: a twin neighbourhood this program's own splice created elsewhere does not make the carried anchor ambiguous, and an anchor the program never bound resolves against current state. Normalization is the same line-content representation used by READ and line hashing; file-write revision checks remain independent. No approximate text matching is used. Lowered coordinates retain a current-anchor precondition at the mutation owner; ambiguous matches and concurrent changes remain collisions.
1616
- - §anchor-offset **An anchor offset is tolerated, never taught (#749).** A line mark may carry an offset from its anchor (`@abcde+1`, `@abcde-2`), and a bare `+N` after an anchor counts from that anchor (`<@abcde,+1>`). The anchor resolves as usual and the offset is added; a result before line 1 is an invalid mark, and past the end is the ordinary range refusal. Continuity and current-anchor preconditions check the anchor's own line. No teaching text, scope table or receipt mentions offsets; `plurnk.md` keeps its two anchor forms. A bare `+N` with no anchor before it is refused as before.
1643
+ - §anchor-offset **An anchor offset is tolerated, never taught (#749).** A line mark may carry an offset from its anchor (`@abcde+1`, `@abcde-2`), and a bare `+N` after an anchor counts from that anchor (`<@abcde,+1>`). The anchor resolves as usual and the offset is added; a result before line 1 is an invalid mark, and past the end is the ordinary range refusal. Continuity and current-anchor preconditions check the anchor's own line. `plurnk.md` keeps its two anchor forms. Numeric relative endpoints normalize under {§scope-range-recovery}; a reversed range produced by anchor resolution is never reinterpreted as a count.
1617
1644
  - §edit-batch **One compound operation may require atomic splices.** The scheme's `editBatch` primitive validates all supplied numeric edits against one snapshot and commits one revision or none. Core supplies one statement for an authored EDIT; same-resource MOVE can supply multiple splices as one operation. This primitive does not group separate authored operations. Its replacement, insertion, conflict, and receipt rules remain owned by the shared Slicer.
1618
1645
  - §edit-batch-receipt **A refusal describes its own unapplied work.** An anchor collision lists every distinct unresolved anchor in that EDIT, including both range endpoints, in `unresolvedAnchors` (`anchor`, `kind: missing | ambiguous`, and matching `lines` when ambiguous). Missing is not proof of earlier validity or subsequent change. It carries `editCount: 1`, `applied: 0`, and recovery directing a READ for current coordinates; it makes no claim about other operations. A refused compound splice batch lists all conflicting pairs in `conflicts`, non-conflicting regions in `cleanRegions`, its first pair in `conflictingRegions`, and its own `editCount` and `applied: 0`.
1619
1646
  - §edit-batch-merges **Normalizations require evidence and a receipt.** An EDIT body carrying only this resource's published `L<@xxxxx>` prefixes (the number right-aligned) is stripped when those prefixes verify against current anchors or this worker's preserved READ receipts (`rendered-prefix-stripped`); otherwise it remains literal content (`rendered-prefix-unverified`). Within a single atomic splice batch, the Slicer can deduplicate identical regions/bodies, concatenate same-boundary insertions, assign a shared endpoint to the sole body reproducing that line, or relocate an inner change when its original content occurs exactly once in the outer body. An already-applied inner body can be dropped. Unevidenced overlap remains a collision. These batch resolutions never reinterpret separate authored EDITs. Applied normalizations carry their exact merge facts and a notice; receipts describe only the applied effects.
@@ -2035,24 +2062,24 @@ AST: `{ op: "EDIT", target, body: string | null, signal: tags | null, lineMarker
2035
2062
  ```` ```EDIT (path) [{"pattern": "/foo/"}] ```` reads the resource once under
2036
2063
  the channel's own mimetype, matches, and expands into one atomic batch
2037
2064
  ({§edit-batch}) of four-coordinate splices, all relative to the same original
2038
- content: a regex span is its evidence region; a literal (a glob without
2039
- metacharacters) is each of its occurrences on every matched line; a glob with
2040
- metacharacters is the whole matched line; a node dialect's span (`//` xpath,
2041
- `$` jsonpath) is the node's whole region as its handler reports it, across as
2065
+ content under {§slice-semantics-compose-pattern}: a regex span is its match;
2066
+ a literal is each substring occurrence; a glob selects matching line content;
2067
+ a node dialect's span (`//` xpath, `$` jsonpath) is the selected node's source
2068
+ region, not its enclosing context, across as
2042
2069
  many lines as the node spans, so ```` ```EDIT (books.xml) [{"pattern":
2043
2070
  "//book[price > 35]"}] ```` replaces each such element and an empty body
2044
- removes it. The body is literal replacement text, never a template; an absent
2045
- body deletes the spans and leaves their lines. A regex anchors each line (`^`,
2046
- `$`) and a regex span never crosses a line break — a match that would is
2047
- refused before any change (400 `pattern-span-invalid`). A numeric scope bounds the lines
2048
- a pattern may touch; `<0>` and `<-1>` name positions, not lines, and are refused
2071
+ removes it. FTS uses its located token/phrase occurrences; graph uses the
2072
+ definitions or references selected under {§graph-relations}. The body is
2073
+ literal replacement text, never a template; an absent body deletes only the
2074
+ spans. Regex anchors apply per line (`^`, `$`); matches may cross line breaks.
2075
+ A numeric scope admits only complete matches inside its resolved source
2076
+ region, including columns; `<0>` and `<-1>` name insertion positions and are refused
2049
2077
  (400 `pattern-scope-invalid`). Every touched line's anchor guards the batch as a
2050
2078
  precondition, so a same-turn change to one of them is the ordinary
2051
2079
  {§edit-collision}. Zero matches change nothing: 204 with `matched: 0`, never a
2052
2080
  clobber. The result is one operation receipt: `matched` spans, `receipt` for the
2053
2081
  first splice ({§edit-result-receipt-projection}), and `last` beside it for the
2054
- final one when there were several. Resource-selecting dialects (`~`, `&`)
2055
- name no spans: 400 `pattern-dialect-unsupported`. A scheme without textual EDIT scopes refuses the
2082
+ final one when there were several. A scheme without textual EDIT scopes refuses the
2056
2083
  pattern before any read (400 `pattern-unsupported`). Same-turn anchor continuity
2057
2084
  ({§edit-anchor-continuity}) does not carry through a pattern batch; the next
2058
2085
  anchored EDIT validates against current state.
@@ -2083,25 +2110,20 @@ READ is the one fan-out core performs ({§read-fan-out}).
2083
2110
  - §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.
2084
2111
  - §read-pattern **A pattern selects the lines a READ renders.** With a heading
2085
2112
  matcher ({§matcher-option} in the contracts SPEC) an exact-target READ stays a
2086
- READ: the matcher runs over the channel's text line by line — a regex anchors
2087
- each line, so `^` and `$` are the line's ends — and every line a match touches,
2113
+ READ: the matcher runs over the channel's complete text — a regex anchors
2114
+ each line, so `^` and `$` are the line's ends, while matches may span lines — and every line a match touches,
2088
2115
  in source order, is the visible selection. The scope still bounds it: a scoped
2089
2116
  READ renders exactly the selected lines the scope holds; a whole-resource one
2090
2117
  pages through the selected lines under the ordinary preview bound, never showing
2091
2118
  an unselected line. Selected lines keep their physical ordinals and their
2092
2119
  ordinary anchors, so a pattern READ is a coordinate source for EDIT and KILL. The
2093
2120
  result carries `matched`, the count of selected lines inside the scope. Zero
2094
- matches is an empty read (204, `matched: 0`), never a failure. A full-text
2095
- (`~`) or graph (`&`) pattern selects resources, not lines: 400
2096
- `pattern-dialect-unsupported` ({§pattern-dialect-find-only}); a matcher its mimetype cannot run answers the
2097
- matcher's own 415/400 ({§matcher-dispatch}).
2098
- - §pattern-dialect-find-only **A `~` or `&` matcher outside FIND is refused as FIND's alone.** Every
2099
- 400 `pattern-dialect-unsupported` — READ, EDIT, KILL, COPY, MOVE, SEND — names the model's
2100
- matcher and says only FIND takes it, and its recovery gives both working forms with the
2101
- model's own target: the FIND carrying that matcher, and the same operation with a text
2102
- pattern built from the symbol or words it named (regex metacharacters escaped, words joined
2103
- by `|`): `` Locate it with `FIND (django/urls/resolvers.py) &RoutePattern`, or select lines
2104
- with a text pattern: `READ (django/urls/resolvers.py) /RoutePattern/`. ``
2121
+ matches is an empty read (204, `matched: 0`), never a failure. Full-text and
2122
+ graph matches project their located occurrences through the same line
2123
+ presentation. A matcher its mimetype cannot run answers the
2124
+ matcher's own 415/400 ({§matcher-dispatch}). A selected value with neither
2125
+ exact nor enclosing source coordinates answers 422 `pattern-source-unlocated`;
2126
+ READ does not substitute an unrelated ancestor or the whole resource.
2105
2127
  - §read-fan-out **A READ over a glob reads every matching path.** `READ (pets_*.md)`
2106
2128
  and `READ (pets_*.md) /dogs/i` keep their glob ({§read-find-normalization} in the
2107
2129
  contracts SPEC) and dispatch fans them out: the ordinary FIND over the same
@@ -2120,8 +2142,24 @@ READ is the one fan-out core performs ({§read-fan-out}).
2120
2142
  receipt on the authored glob (`matched: 0` when a pattern selected nothing); a
2121
2143
  FIND failure is that failure on the authored glob. The FIND's resource page bounds
2122
2144
  the fan-out: when more paths matched than were read, one `read_fanout_bounded`
2123
- notice names both counts. A full-text (`~`) or graph (`&`) matcher selects
2124
- resources, not lines, so that READ dispatches as the FIND survey.
2145
+ notice names both counts. Every dialect follows this same READ fan-out;
2146
+ none substitutes a FIND receipt for the requested content.
2147
+
2148
+ §read-pattern-evidence **Match regions survive line-oriented presentation.** A patterned
2149
+ READ retains `matches` ({§matcher-selection-signal}) for findings intersecting its
2150
+ returned text, in physical source coordinates. The body shows matching lines;
2151
+ its `range` describes that presentation, not the match boundaries. A match's
2152
+ region is never clipped into a different selection when a preview or authored
2153
+ READ scope shows only part of it. An empty read carries an empty match array.
2154
+
2155
+ The packet projects each visible match's `locator`, exact `region`, or
2156
+ explicitly enclosing `enclosingRegion`
2157
+ using {§packet-extent-metadata}, without duplicating matched body text. Log
2158
+ curation removes metadata for matches outside the retained body. Match metadata
2159
+ uses the ordinary preview allowance ({§body-projection}), keeping only complete
2160
+ items; `matchLocationCount` appears when that preview omits locations. Exact
2161
+ FIND pages the complete location set ({§find-result-projection}).
2162
+
2125
2163
  - §read-bytes A binary channel, and the `#bytes` view of
2126
2164
  any resource whose scheme supplies bytes, reads as the source bytes one hexadecimal
2127
2165
  octet per line: coordinate = line = byte, so `<a,b>` selects bytes, the markerless
@@ -2141,34 +2179,24 @@ READ is the one fan-out core performs ({§read-fan-out}).
2141
2179
  into a byte READ (`region` spans the hexadecimal lines of the matched bytes; `matched`
2142
2180
  is their hex). The load is bounded by the mimetypes binary input ceiling; a larger
2143
2181
  resource fails 413 `bytes-too-large` by name rather than being skipped.
2144
- - §binary-parity A binary member is not a second-class resource. It behaves exactly as a text
2145
- member does for existence, FIND by path, KILL/delete, mimetype, weight, and membership; it
2146
- READs whole as its byte projection ({§read-bytes}) and, on a supporting route, contributes native
2147
- content to the next model request ({§packet-attachment-parts}),
2148
- READs and FINDs by byte range and byte pattern ({§read-bytes}/{§find-bytes}); and COPY or MOVE transfers
2149
- its bytes exactly, between file members and into or out of a DB-backed `worker://` entry alike. A
2150
- whole-resource transfer writes the source's bytes ({§read-bytes} `ByteSource`) verbatim to the
2151
- destination through the ordinary proposal gate, the receipt reporting the byte count rather than a text
2152
- line diff; "whole-resource" is the markerless selection or `<1,-1>` ({§move-canonical-whole-source}),
2153
- and a MOVE deletes the source after the destination lands. A **byte range** `<a,b>` transfers exactly
2154
- those source bytes (coordinate = byte, 1-indexed inclusive). A transfer **into** a destination byte
2155
- range is a splice: `<c,d>` replaces exactly the destination bytes c..d with the source bytes and a
2156
- single position `<c>` inserts the source bytes before byte c (`<-1>` appends); every byte outside the
2157
- window is preserved, and the whole spliced result is re-written through the proposal gate. A binary
2158
- **lives in a DB entry** as its bytes base64 in the channel's TEXT content; the same READ, byte range,
2159
- and COPY/MOVE recover them through a byte source synthesized from that content, so a File member and a
2160
- `worker://` entry hold and yield a binary identically. An empty binary channel represents
2161
- zero bytes, not an unsupported format; whole COPY/MOVE preserves it. Failed acquisition
2162
- is not an empty success: its producer outcome remains authoritative for READ and transfer.
2163
- This supersedes the older blanket refusal (#140)
2164
- for both the file and the entry case. Native image/PDF/audio attachment facts come from the configured
2165
- mimetype handler over original bytes, whether supplied by a file or stored channel
2166
- ({§packet-attachment-parts}); the hexadecimal view remains available. The exceptions are narrow and
2167
- defined, each a clear receipt rather than a dead end: a binary region addressed by a **textual anchor**
2168
- rather than a numeric byte coordinate has no meaning (416 — bytes are not lines), **authoring** binary
2169
- content from a text EDIT body is impossible (a text emission cannot type bytes), and a scheme that keeps
2170
- no bytes for a binary channel — no disk file, no stored content — has nothing to transfer and says so
2171
- (415). None is the entry-storage dead end the older text named; that cell is filled.
2182
+ - §binary-parity Binary resources share ordinary existence, membership, FIND-by-path,
2183
+ deletion, and proposal rules. Files supply native bytes; DB-backed channels store
2184
+ them as base64 and recover the same bytes through their byte source. Empty binary
2185
+ content is zero bytes, never an unsupported format. Acquisition failure preserves
2186
+ its producer result and cannot become a successful empty transfer.
2187
+
2188
+ | Binary operation | Contract |
2189
+ | --- | --- |
2190
+ | READ / FIND | Hexadecimal byte projection and byte-pattern coordinates under {§read-bytes}/{§find-bytes}. Eligible image/PDF/audio attachments use original bytes independently ({§packet-attachment-parts}). |
2191
+ | Whole COPY / MOVE | Markerless or `<1,-1>` transfers the original bytes verbatim ({§move-canonical-whole-source}); MOVE deletes its source after the destination lands. |
2192
+ | Source range or pattern | `<a,b>` selects 1-indexed inclusive bytes. Patterns use {§find-bytes}; a source scope admits complete matches. Fragments concatenate without separators. MOVE removes exactly those bytes after destination success. |
2193
+ | Destination range | `<c,d>` replaces bytes c..d; `<c>` inserts before byte c, `<0>` prepends, and `<-1>` appends. All coordinates name the pre-mutation source. Other bytes remain unchanged. |
2194
+ | Same-channel MOVE | Destination insertion and source removal use one pre-mutation snapshot and one proposal-gated write. Overlapping selections refuse with 409 before writing. |
2195
+ | Deferred MOVE | The source byte snapshot is retained as a content-hash precondition; changed bytes refuse removal with 409 `edit-collision`. A landed destination remains reported under {§copy-move-observation}. |
2196
+ | Mutation result | Ordinary proposal gate and byte count; no fabricated text diff. |
2197
+ | Text anchor on a transfer | 416: native byte coordinates are numeric. |
2198
+ | Text EDIT of native bytes | 415: an EDIT body authors text, not native bytes. |
2199
+ | Binary channel without a byte source | 415, not an empty success. |
2172
2200
 
2173
2201
  ### §log-history-projection Durable history and active projection
2174
2202
 
@@ -2250,7 +2278,11 @@ Coordinates and anchors retain the original body's physical lines; selection occ
2250
2278
  before omitted lines are removed, and sparse receipts retain their original line
2251
2279
  ordinals. Automatic previews remain retrieval bounds, not deletions. COPY can read
2252
2280
  an active log source under ordinary read authority, without minting log history;
2253
- destinations and MOVE sources still require independently writable entry storage.
2281
+ entry destinations require independently writable storage; MOVE retires its
2282
+ source under {§move-decomposition}. A pattern spanning retained lines on either
2283
+ side of a curated gap maps to separate exact source regions, never to a bounding
2284
+ region that restores hidden lines. Concatenating those fragments reproduces only
2285
+ the matched readable text.
2254
2286
  Trimming invalidates derived search attachments; an in-flight derivation attaches
2255
2287
  only if its source projection is still current. Forks copy both projection facts;
2256
2288
  forensics always retain the complete immutable body and curation history.
@@ -2371,7 +2403,7 @@ The packet projects one actionable owner for each retrieval fact:
2371
2403
  | catalog/path FIND | `range` in resources | none | none |
2372
2404
  | broad matcher FIND | `range` in resources | per-resource match-location counts; a resource with exactly one match also carries that match's `locator`/`region` | nonzero complete `matchLocationCount` |
2373
2405
  | exact matcher FIND | `range` in match locations | each row's locator/region; a regex or glob row also carries `matched`, the matched text | none |
2374
- | pattern READ ({§read-pattern}) | `range` over the physical lines | the selected lines with their ordinals and anchors | the heading's pattern and `matched`, the selected line count |
2406
+ | pattern READ ({§read-pattern}) | `range` over the physical lines | selected lines with ordinals and anchors; precise `matches` under {§read-pattern-evidence} | heading pattern and selected-line `matched`; `matchLocationCount` only when the metadata preview omits locations |
2375
2407
 
2376
2408
  Any row whose statement carried a heading pattern ({§matcher-option}) retains it
2377
2409
  in its H3, and a pattern mutation ({§edit-pattern}, {§kill-pattern},
@@ -2588,24 +2620,23 @@ Operand syntax: {§transfer-resource-selections}. Result projection: {§copy-mov
2588
2620
  channel is 404. Entry sources follow {§membership-source-projection}; active
2589
2621
  log sources follow {§log-readable-projection}. Binary sources transfer bytes
2590
2622
  under {§binary-parity}; text anchors resolve under {§line-anchors}.
2591
- - §copy-move-pattern **A source pattern selects whole matching lines; a
2623
+ - §copy-move-pattern **A source pattern selects exact source spans; a
2592
2624
  destination is a place.** A source operand's heading pattern
2593
2625
  (```` ```COPY (notes.md) [{"pattern": "TODO"}] (todos.md) <-1> ````) runs
2594
- over the source text line by line, bounded by the source scope and by what
2595
- the source shows ({§log-readable-projection}); the selection is every line a
2596
- match touches, in source order, each with its own line separator exactly as
2597
- a scoped whole-line selection carries it. The result reports `matched`. Zero
2626
+ over the addressed source channel, bounded by the source scope and by what
2627
+ the source shows ({§log-readable-projection}). The selected fragments are
2628
+ concatenated verbatim in source order, without invented separators or
2629
+ enclosing line context ({§slice-semantics-compose-pattern}). The result
2630
+ reports `matched` spans. Zero
2598
2631
  matches transfer nothing: 204 with `matched: 0`, and no destination is
2599
- created. A MOVE retires exactly the selected lines through the source's EDIT
2600
- path, one empty-body line splice per line in one batch guarded by their
2601
- anchors ({§edit-pattern}); within one channel the insertion and the removals
2632
+ created. A textual MOVE removes exactly those spans through the source's EDIT
2633
+ path, one empty-body exact splice per span in one batch guarded by their
2634
+ anchors ({§edit-pattern}); byte selections use {§binary-parity}. Within one channel the insertion and the removals
2602
2635
  are one atomic batch, and a deferred MOVE ({§proposal}) retires the same
2603
- lines after acceptance. A curated source retires rows, not lines of its
2636
+ spans after acceptance. A curated source retires rows, not arbitrary text of its
2604
2637
  projection, so a pattern MOVE from `log:///` is 400 `pattern-unsupported`
2605
- (COPY the lines, then KILL its rows by pattern); a pattern on a binary
2606
- channel is 400 `pattern-unsupported` (bytes have no lines); a pattern on
2607
- the destination is 400 `pattern-destination-unsupported`; resource-selecting
2608
- dialects (`~`, `&`) are 400 `pattern-dialect-unsupported`.
2638
+ (COPY its selected text, then KILL its rows by pattern). A pattern on
2639
+ the destination is 400 `pattern-destination-unsupported`.
2609
2640
  2. Resolve destination path, channel, and optional text scope. Source and
2610
2641
  destination mimetypes must be compatible under {§mimetype-verbatim-transfer}
2611
2642
  or the result is 415. Destination anchors
@@ -2768,7 +2799,7 @@ same durable liveness.
2768
2799
  | New unpublished message | Continue; publish it in the next packet. |
2769
2800
  | Fresh operation/parser failure, without an authored WAIT | Continue before any automatic parking. |
2770
2801
  | Neither an authored WAIT nor an eligible completion request ({§kill-conclusion}) | Continue, regardless of earlier replies or live work. |
2771
- | Live work and either WAIT or an eligible completion request | Park the same loop; message arrival, child or stream settlement, or stream cadence wakes it. No final-answer body is delivered while joining. |
2802
+ | Live work and either WAIT or an eligible completion request | Park the same loop; message arrival, child or stream settlement, or wait expiry wakes it ({§worker-wait-timing}). No final-answer body is delivered while joining. |
2772
2803
  | WAIT without live work | Continue; never invent a future wake. Its row says only `Nothing is in flight. Continuing.`, however often the loop yields this way. |
2773
2804
  | Unobserved operation results, failures, child results or stream conclusions | Continue; the next packet presents them. |
2774
2805
  | Eligible completion request with no unpublished arrivals, live work or unobserved results | Conclude successfully, whether or not it delivers an answer. |
@@ -3115,14 +3146,10 @@ executor target is refused `scope-unsupported` (400), naming the field.
3115
3146
  | `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}). |
3116
3147
  | `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. |
3117
3148
 
3118
- **Cadence is the daemon's, never the model's.** While a loop is parked on an open
3119
- stream the daemon wakes it on the worker's exponential backoff
3120
- (`PLURNK_SERVICE_EXEC_POLL_SEC`, `PLURNK_SERVICE_EXEC_POLL_TURNS`, floored by
3121
- `PLURNK_SERVICE_OPTIMISTIC_WAIT_MS`) to inspect progress; it does nothing while
3122
- the loop is active, because ambient stream deltas already surface progress.
3123
- Closure is a wake edge regardless. Child-only joins never use this timer: child
3124
- settlement is their durable wake edge. A recurring check on the calendar is a
3125
- schedule targeting yourself ({§schedule-delivery}), not a loop that polls.
3149
+ Observation timing belongs to the bounded wait ({§worker-wait-timing}), separately
3150
+ from execution lifetime. Wait expiry permits inspection of still-running work;
3151
+ closure remains an independent wake edge. Active loops already receive ambient
3152
+ stream deltas. Calendar recurrence remains a schedule ({§schedule-delivery}).
3126
3153
 
3127
3154
  §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}).
3128
3155
 
@@ -3188,7 +3215,7 @@ two states and no others:
3188
3215
 
3189
3216
  | state | what the model receives |
3190
3217
  |---|---|
3191
- | active | nothing in the Log. The `## Delegation` stream pointer names the stream with each channel's size and its growth since the last packet ({§child-orientation}); the model READs any range it wants, and every READ of a stream channel carries `terminal: false` while it runs and `terminal: true` once it has concluded, so an empty page is never mistaken for a finished command that printed nothing. |
3218
+ | active | nothing in the Log. The `## Delegation` stream pointer reports timing and channel activity ({§child-orientation}); the model READs any range it wants, and every READ of a stream channel carries `terminal: false` while it runs and `terminal: true` once it has concluded, so an empty page is never mistaken for a finished command that printed nothing. |
3192
3219
  | terminal | ONE `origin=_plurnk` READ at the execution's channel address, born visible, that is exactly a markerless READ of the channel — its bounded first page ({§read-selection-projection}, the whole channel when it fits, the channel's own mimetype), the `range` or `region`, terminal status and Problem, `terminal: true`, and any producer-supplied integer `exitCode`. The packet writes the read resource as its operand, exactly as an explicit READ does ({§log-address-metadata}). |
3193
3220
 
3194
3221
  §stream-observation-result **One liveness fact.** The durable READ result owns
@@ -3517,6 +3544,20 @@ Capability admission precedes this decision, so proposal disposition cannot gran
3517
3544
 
3518
3545
  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.
3519
3546
 
3547
+ §subscription-finalization Executors and scheme subscriptions share the durable
3548
+ close boundary. Concurrent closes join one attempt; a committed close is
3549
+ idempotent, including its completion wake.
3550
+
3551
+ | Boundary | Ownership and recovery |
3552
+ | --- | --- |
3553
+ | Persistence fails | Surface the cause, retain the terminal result and callable owner, and emit no completion wake. Another close or cancellation retries settlement, not execution. |
3554
+ | Persistence commits | Release ownership before observational delivery. Attempt channel notifications and the completion wake even if another observer throws. |
3555
+ | Observer fails after commit | Surface the cause without reverting terminal state, retaining a false live owner, or repeating delivery on another close. |
3556
+
3557
+ Cancellation remains coalesced while work is live. Failed cancellation or
3558
+ failed settlement permits a subsequent cancellation attempt; it never converts
3559
+ the producer's already obtained outcome into a fictitious cancellation result.
3560
+
3520
3561
  At process restart every still-open row is necessarily missing its callable owner. Boot
3521
3562
  settles it as interruption (`500`) and errors active channels before evaluating parked
3522
3563
  loops ({§worker-lifecycle-restart-recovery}); it never reports cancellation (`499`) or
@@ -4101,10 +4142,13 @@ The shared deadline bounds every phase, including observer delivery; forced
4101
4142
  shutdown may therefore lose notifications and reports the unfinished phase.
4102
4143
 
4103
4144
  §crash-only-stop The settle sequence is deadline-bounded
4104
- (`PLURNK_SERVICE_STOP_TIMEOUT_MS`, default 30000): past the deadline each wait
4145
+ (`PLURNK_SERVICE_STOP_TIMEOUT_MS`): one absolute deadline spans daemon drains
4146
+ and the enclosing observability, database, and HTTP-listener cleanup. No phase
4147
+ renews the budget. The service joins core's bounded producer/observer drain
4148
+ before releasing enclosing resources, including on timeout. Past the deadline each wait
4105
4149
  is abandoned with a named error instead of hanging the daemon on a child that
4106
4150
  never closes. A wedged child costs a forced shutdown; it must never cost an
4107
- unbounded one. When the teardown settles, success or failure, the process ends
4151
+ unbounded one. Later cleanup phases are still attempted. When the teardown settles, success or failure, the process ends
4108
4152
  itself: `0` after a clean teardown, `1` after a reported one. A handle an abandoned
4109
4153
  wait left alive never keeps a stopped daemon running; the supervisor's kill is a
4110
4154
  backstop, not the exit.
@@ -4162,8 +4206,13 @@ transfer, or remove ownership of workspace tools or shared resources.
4162
4206
 
4163
4207
  §module-workspace-quiescence **A Functionality snapshot changes between
4164
4208
  workspace operations.** Mutation admission, external installation/removal,
4165
- preparation, and publication hold the same exclusive gate. Explicit client
4166
- mutations use `try` and fail 409 before effects while the workspace is held.
4209
+ preparation, and publication hold the same exclusive gate. Every mutation or
4210
+ background refresh acquires that gate before the family's serialization lane;
4211
+ turn-admission refresh uses its already-held gate. A queued background refresh
4212
+ cannot own the family lane while waiting for a turn that needs that lane.
4213
+ Settling the coordinator includes accepted refreshes waiting for that gate,
4214
+ not only work already inside a family lane.
4215
+ Explicit client mutations use `try` and fail 409 before effects while the workspace is held.
4167
4216
  Refused mutations reserve no queue position; a client retry is a new admission.
4168
4217
  An accepted model mutation uses `wait`, proceeding after its originating turn. Activation and turn-admission refresh use `none` inside
4169
4218
  their already-held demand boundary. Providers reject replacement while active
@@ -4518,7 +4567,9 @@ execution stream.** Preparation and publication share the family's serialized
4518
4567
  lane. Publication acquires workspace exclusivity after current turns release
4519
4568
  their leases; the invoking stream remains pending until publication completes.
4520
4569
  It then reports `active`, `unavailable`, or `authorization-required`, or the exact
4521
- publication failure. A preparation failure may publish enabled-but-unavailable
4570
+ publication failure. Resource readiness is not execution liveness: a mutation's
4571
+ `202 authorization-required` body remains exact, while the finished manager
4572
+ execution closes with `200`. It does not wait for sign-in. A preparation failure may publish enabled-but-unavailable
4522
4573
  state; a publication failure never reports a successful mutation. Stream polling,
4523
4574
  waiting, and result observation use the ordinary execution lifecycle, without a
4524
4575
  separate deferred-commit queue. An explicit client action publishes now, rejects
@@ -4970,9 +5021,16 @@ time of measurement.
4970
5021
  - §tokenomics-neutral-telemetry **Curation telemetry is state, not response allowance.** The model-facing `Context Curation` section is one JSON object carrying `logTokensTotal` and `logTokensMax`. It never presents their difference as free response tokens. The protocol definition directly requires KILL of irrelevant log items and ranges to keep the next packet within the maximum. Per-entry weights remain on log rows where they describe visible cost and curation savings. Generic packet composition and physical-token speculation are absent.
4971
5022
  - §tokenomics-pressure-inventory **Pressure identifies its reclaimable concentration.** At `PLURNK_SERVICE_BUDGET_PRESSURE` of `logTokensMax`, a Markdown `> [!WARNING]` block follows the JSON with `> YOU MUST KILL superseded, stale, or irrelevant log items and ranges.` New output withholding replaces that mandate under {§context-output-warning}. The JSON may include `logTokensLargest`: at most `PLURNK_SERVICE_BUDGET_LARGEST_ITEMS` retained log items, each `{path, logTokens}`, ranked by that charge descending and then path. Native-only, suppressed, and metadata-only items remain eligible: whole-item KILL can reclaim their actual contribution. Include the largest prefix that fits; drop the optional list before the warning. Both participate in the final fixed-point total and complete request admission check.
4972
5023
  - §tokenomics-content-hash-identity **Content identity, not per-tokenizer counts.** A settled channel's `content_hash` (SHA-256) is its body's identity in the content store ({§content-store}); a writer may bind it, a bound hash must match the content, and an active stream has none until it settles. `weight` is stored beside that content and is never keyed or recomputed by model.
4973
- - §tokenomics-provider-usage **Provider accounting is physical-request evidence, not curation state.** Every issued physical request has one durable pre-I/O `provider_requests` identity beneath the normalized {§inference-ledger} and settles once as response or error. Each record preserves conventional {§provider-usage} quantities and required {§provider-cost} evidence; an unreported quantity remains absent, including on response-less failures, and is never replaced by zero. `model_calls` own response/failure evidence, `turn_attempts` specialize emission admission, and `provider_requests` are the sole durable accounting representation. Emissions, BARE calls, rejected responses, retries, failovers, and errors therefore remain cardinal and ordered. Turn, loop, worker, workspace, digest, and protocol accounting are derived from those records through the shared {§provider-accounting} projection; only emission calls contribute the latest-packet context gauge. The baseline stores no floating-point money, denormalized totals, or rollup triggers. A documented direct charge becomes `charged`; otherwise the provider may compute an exact-decimal USD `estimated` amount from complete usage and the exact model's Models.dev rates; insufficient evidence becomes `unknown`. Derived `costUsd` sums every USD-expressible request and is `null` only when no request is expressible; a response-less failure or an uncataloged model is skipped, never allowed to erase the expressible evidence. Each derived aggregate usage field independently sums its reported quantity, so heterogeneous detail coverage remains partial rather than becoming fictitiously complete. This is operational request accounting, not invoice reconciliation. Output and reasoning are quantities the model cannot KILL, so they never alter the model-facing Budget ledger.
5024
+ - §tokenomics-provider-usage **Provider accounting is physical-request evidence, not curation state.** Every issued physical request has one durable pre-I/O `provider_requests` identity beneath the normalized {§inference-ledger} and settles once as response or error. Each record preserves conventional {§provider-usage} quantities and required {§provider-cost} evidence; an unreported quantity remains absent, including on response-less failures, and is never replaced by zero. `model_calls` own response/failure evidence, `turn_attempts` specialize emission admission, and `provider_requests` are the sole durable accounting representation. Emissions, BARE calls, rejected responses, retries, failovers, and errors therefore remain cardinal and ordered. Turn, loop, worker, workspace, digest, and protocol accounting are derived from those records through the shared {§provider-accounting} projection; only emission calls contribute the latest-packet context gauge. The baseline stores no floating-point money, denormalized totals, or rollup triggers. A documented direct charge becomes `charged`; otherwise the provider may compute an exact-decimal USD `estimated` amount from complete usage and the exact model's Models.dev rates; insufficient evidence becomes `unknown`. Complete totals and known subtotals remain distinct under {§provider-accounting}; unknown requests cannot impersonate free requests, and missing usage never becomes zero. This is operational request accounting, not invoice reconciliation. Output and reasoning are quantities the model cannot KILL, so they never alter the model-facing Budget ledger.
4974
5025
  - §tokenomics-negative-pressure **Negative curation pressure is honest but never submitted.** The provisional readout may report `logTokensTotal` above `logTokensMax`. Crossing the maximum withholds new returned output under {§context-output-admission}; no over-ceiling packet reaches `provider.generate`. Output admission creates neither a strike nor another turn.
4975
5026
 
5027
+ Each physical request also retains {§provider-request-evidence}, atomically
5028
+ with its accounting settlement. Failure does not discard received fragments.
5029
+ The digest emits one linked `requests/<id>.json` artifact per physical request,
5030
+ including unavailable evidence as `null`, reading heavy records one at a time.
5031
+ Failed partial operations and reasoning remain forensic data only; they never
5032
+ execute, enter model history, or become a later request's successful output.
5033
+
4976
5034
  ### §context-output-admission Budget enforcement: returned-output admission
4977
5035
 
4978
5036
  Operations and their results are execution history. Packet admission controls
@@ -5377,7 +5435,7 @@ retain distinct contracts and lifetimes.
5377
5435
  - **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`.
5378
5436
  - **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.
5379
5437
  - **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.
5380
- - §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. 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.
5438
+ - §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.
5381
5439
  - §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.
5382
5440
 
5383
5441
  **The error rows (one channel) + the only non-log notices:**
@@ -5401,7 +5459,20 @@ retain distinct contracts and lifetimes.
5401
5459
 
5402
5460
  §notice-event-notify **Client surface.** Engine Notices broadcast live via the `notice/event` notification — `{ workerId, loopId, notice: { source, kind, level, message?, position?, …kind-specific } }` per the grammar's `Notice` schema — the moment they land. A loop Notice names its owning Worker; workspace derivation progress alone carries `workerId=null, loopId=0`. AG-UI projects the same observation as the custom `plurnk.notice` event. Failures do not broadcast on this surface: they are log rows, and the client reads them through `log.read` / the `log/entry` notification, the durable log.
5403
5461
 
5404
- §loop-status-notice **The drain beats the loop's lifecycle on the notice channel.** When the drain claims a loop to run it broadcasts `notice/event` `{ workerId, loopId, notice: { source: "engine:lifecycle", kind: "loop_status", level: "info", status: 102 } }`, and when it leaves a loop parked ({§loop-wake-identity}) the same with `status: 202`; a wake that the drain claims again is another `102`. The beat is transient: broadcast to the workspace like any notice ({§notice-event-notify}), never a log row, never in a packet. Terminals stay `loop/terminated`'s; a client that reads the beat has the running/parked edges a parked delegation otherwise never publishes.
5462
+ §loop-status-notice **The drain publishes lifecycle and its observation deadline together.**
5463
+ `notice/event` carries `{ workerId, loopId, notice: { source: "engine:lifecycle", kind: "loop_status", level: "info", status, waitUntil } }`.
5464
+
5465
+ | Edge | `status` | `waitUntil` |
5466
+ |---|---|---|
5467
+ | Claimed to run | 102 | `null` |
5468
+ | Parked | 202 | Persisted Unix-millisecond observation deadline, or `null` for an untimed park |
5469
+ | Woken, awaiting dispatch | 100 | `null` |
5470
+
5471
+ Park observations and wakes use the worker's existing drain serialization, so
5472
+ an earlier park cannot overwrite a later wake's cleared countdown.
5473
+ The beat is transient: broadcast to the workspace ({§notice-event-notify}), never
5474
+ a log row or packet. Terminals remain `loop/terminated`'s. Reattachment obtains
5475
+ the same deadline through {§application-loop-observation}, not a restarted clock.
5405
5476
 
5406
5477
  §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.
5407
5478
 
@@ -5486,11 +5557,13 @@ final packet and the newest attempts always testify. A windowless witness
5486
5557
 
5487
5558
  §turn-accounting-notice **The completion beat carries the spend.** `turn_generated`
5488
5559
  carries the turn's exact settled wire accounting — request count, exact nullable
5489
- USD, and token totals across every physical exchange the turn paid for, failed
5560
+ USD and token totals with separately named known subtotals across every physical exchange the turn paid for, failed
5490
5561
  calls included. It is the shared exact derivation from the ledger, never a second
5491
5562
  stored fact, so a live watcher accrues running loop cost per turn (#465).
5492
5563
 
5493
5564
  §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.
5565
+ Reasoning normalization notices use source `grammar:reasoning` and omit the
5566
+ content-offset position: reasoning coordinates do not address the content source.
5494
5567
 
5495
5568
  ### Executable tool resources
5496
5569
 
@@ -5543,6 +5616,12 @@ with a multiline regex over matching fences), so turn 0 names every tool with it
5543
5616
  signature — one row per tool, paged like every survey. Capability attenuation
5544
5617
  restricts that matcher to the admitted exact tools. No document is delivered
5545
5618
  unasked.
5619
+ When the registry supplies {§executor-tool-catalog} definitions, the family
5620
+ document links to its sibling `<runtime>.json`. The catalog contains exactly
5621
+ the effective tools and is an ordinary `application/json` resource for READ,
5622
+ FIND, and JSONPath selection. It follows the same reconciliation as the family
5623
+ document and per-tool schemas; neither the catalog nor its definitions are
5624
+ automatically included in turn 0 or expanded-tool surveys.
5546
5625
  Attached tools are capabilities like every other runtime; the model never
5547
5626
  learns an origin.
5548
5627
 
@@ -5873,7 +5952,17 @@ container identity.
5873
5952
  | ------------ | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
5874
5953
  | `&<symbol` | In-scope resources that reference `symbol` | Each matching reference's source span |
5875
5954
  | `&>symbol` | In-scope resources defining names referenced by each definition of `symbol` | Each referenced symbol's definition span |
5876
- | `&symbol` | Union of definitions of `symbol`, referrers, and definitions of referenced names | Corresponding definition/reference spans, deduplicated by resource + span |
5955
+ | `&symbol` | In-scope resources defining `symbol` | Each definition's source span |
5956
+
5957
+ Graph storage preserves the handler's complete text coordinates, including
5958
+ reference columns and end positions. Match evidence and source text come from
5959
+ the same immutable derivation. A definition without handler-supplied columns
5960
+ selects its declared whole-line extent; a reference always retains its exact
5961
+ region. Matches are deduplicated by resource and complete region, not line.
5962
+ READ presents whole lines with that evidence ({§read-pattern-evidence}); EDIT
5963
+ replaces the selected region literally, not a semantic rename of the symbol.
5964
+ Upgrades invalidate obsolete derived indexes, never their source content or
5965
+ retained history ({§db-migrations}).
5877
5966
 
5878
5967
  | Result | HTTP status |
5879
5968
  |---|---|
@@ -5901,16 +5990,32 @@ one binary marker to fail a repository-wide text search.
5901
5990
  Glob anchoring (`TODO*` starts-with, `*TODO*` contains, `*.log` ends-with,
5902
5991
  `[Tt]odo*` character class) lives in the mimetypes framework.
5903
5992
 
5993
+ §derivation-in-flight **In-flight derivation and query are live uses.** An exact
5994
+ indexed operation holds its content-addressed artifact from derivation through
5995
+ the completed query. Producers hold artifacts while building and attaching
5996
+ them. Derivation and content collection share one per-database exclusion boundary:
5997
+ parallel uses remain parallel, a collection pass skips these two collectors
5998
+ while a use is active, and a new use waits for an already-running collection.
5999
+ Successful, failed, and cancelled uses release in `finally`; no durable pin or
6000
+ additional retention setting is introduced. The ordinary later pass or drained
6001
+ shutdown collects unattached artifacts under {§retention-policy}.
6002
+
5904
6003
  ### Matcher selection and evidence
5905
6004
 
5906
- - §matcher-selection-signal **Matching carries navigation evidence** - a matcher is a boolean resource predicate. Internally, each selected resource carries `matches: MatchEvidence[]`, where `MatchEvidence` is `{channel?,locator?,region?}`; `channel` names the entry channel the finding was located in and is absent for channel-less resources such as log rows, so line coordinates cannot be mis-attributed across channels of the same resource ({§channel-selection-visibility}). `locator` preserves a structural address without overloading the resource row's `path`; `region` is a complete four-coordinate `TextRegion` only when the finding maps honestly into the exact text the model can READ. Exact duplicate evidence deduplicates. Relation findings map their indexed source spans through the same readable text coordinate index. FIND alone decides whether that grouped selection projects as resource rows or flat locations ({§find-result-projection}); the engine never fabricates a region or guesses which surgical READ the model wants.
6005
+ - §matcher-selection-signal **Matching carries navigation evidence** - each selected resource carries `matches: MatchEvidence[]`, where `MatchEvidence` is `{channel?,locator?,region?,enclosingRegion?}`. `channel` identifies the entry channel and is absent for channel-less resources such as log rows ({§channel-selection-visibility}); `locator` preserves the structural address. `region` is the exact selection; `enclosingRegion` is presentation context only. Both use complete four-coordinate `TextRegion`s in the readable source. Exact duplicate evidence deduplicates. FIND projects this evidence as resource rows or flat locations ({§find-result-projection}); consumers never invent missing precision. An operation requiring exact source fragments refuses a selection containing unlocated or enclosing-only findings with 422 `pattern-source-unlocated`, before any mutation.
6006
+
6007
+ §matcher-index-readiness **Indexed patterns require complete evidence.** A held
6008
+ resource snapshot whose search derivation is excluded or failed answers 422
6009
+ `search-unavailable`, preserving the reason. Graph selection requiring an
6010
+ unsettled relationship universe answers retryable 503 `search-index-incomplete`.
6011
+ Neither condition is a successful empty match.
5907
6012
 
5908
6013
  §matcher-result-resource-selection **A matcher selects resources; it never extracts a value or chooses a retrieval
5909
6014
  window.** Every dialect answers whether a resource matches and may return
5910
- `MatchEvidence { locator?, region? }` ({§matcher-selection-signal}). `locator` is a
5911
- canonical structural locator. `region` is a complete `TextRegion` in the exact
5912
- text the model can READ and may be exact or the smallest honest enclosing
5913
- region. A matcher miss is 204. FIND's target shape projects the selected
6015
+ `MatchEvidence { locator?, region?, enclosingRegion? }` ({§matcher-selection-signal}).
6016
+ `locator` is a canonical structural locator. `region` is an exact selected
6017
+ `TextRegion`; `enclosingRegion` identifies presentation context, not a selection.
6018
+ Both address the physical text the model can READ. A matcher miss is 204. FIND's target shape projects the selected
5914
6019
  resources according to {§find-result-projection}.
5915
6020
 
5916
6021
  | Dialect | Selects | Natural use |
@@ -5946,16 +6051,17 @@ One/two-coordinate line shorthand is newline-aware so deleting a line does not
5946
6051
  leave an empty line. A terminal position after a final newline is an exact
5947
6052
  insertion anchor, not an additional whole line. `<1,-1>` selects all content.
5948
6053
 
5949
- §zero-width-column-one-insert **A zero-width region at column 1 inserts whole lines.** The
5950
- schemes region algebra ({§slicer-text-algebra}) inserts every body verbatim; the missing
5951
- newline is a fence artifact, so core repairs it where a fenced EDIT body becomes inserted
5952
- content, through the one schemes helper `wholeLineBody`, at the mutation and again in the
5953
- receipt and anchor-continuity recomputation so every site sees one body. At `<L,1,L,1>`,
6054
+ §zero-width-column-one-insert **An authored zero-width column-1 scope inserts whole lines.**
6055
+ Core prepares that fenced EDIT body once, using `wholeLineBody`, before handing
6056
+ literal replacements to the scheme. Mutation, receipt, and anchor-continuity
6057
+ calculation all consume that same prepared body. At `<L,1,L,1>`,
5954
6058
  an anchored `<@hash,1,@hash,1>`, or `L` = final line + 1 when the content ends with a
5955
6059
  newline, a non-empty body that does not end in a newline is inserted with the content's
5956
6060
  line separator appended, so `X` at `<2,1,2,1>` into `a\nb` yields `a\nX\nb`. An empty
5957
6061
  body inserts nothing. A zero-width region at any other column stays a byte-exact insert with
5958
- nothing appended. COPY and MOVE transfer source bytes, not a fenced body, and are untouched.
6062
+ nothing appended. A pattern's generated coordinates select exact matches, not
6063
+ authored line-insertion syntax: `/^/` inserts a literal prefix without adding
6064
+ a newline. COPY and MOVE transfer source bytes and are likewise untouched.
5959
6065
  The runtime also tolerates an authored three-coordinate
5960
6066
  `<startLine,startColumn,endLine>` scope, immediately lowers it to the complete
5961
6067
  four-coordinate region ending after the final code point of `endLine`, and
@@ -5974,10 +6080,31 @@ down; overlaps and duplicate insertion boundaries are 409. This is the adopted
5974
6080
  SARIF region/replacement algebra for exact spans and same-snapshot ordering, not
5975
6081
  adoption of the SARIF interchange envelope.
5976
6082
 
5977
- §slice-semantics-compose-pattern **Compose from evidence.** A match region already uses the four-coordinate
5978
- scope shape. A follow-up ```` ```READ (resource) <SL,SC,EL,EC> ```` retrieves that exact
5979
- region. JSONPath/XPath remain locators and matchers; they do not introduce a
5980
- second structural scope or structural EDIT language.
6083
+ §slice-semantics-compose-pattern **One selection, independent of operation.**
6084
+ A pattern runs over the canonical source channel, never a READ receipt's
6085
+ line-ending normalization, preview, coordinate prefixes, or other presentation.
6086
+ A match region uses the four-coordinate source scope. FIND reports it, READ
6087
+ shows its lines with that evidence, EDIT replaces it, COPY transfers it, MOVE
6088
+ transfers then removes it, and a textual KILL removes it. Enclosing display
6089
+ context never enlarges the selected span. Multiple fragments retain source
6090
+ order and literal text; COPY/MOVE insert no separators between them. Scope
6091
+ composition admits complete matches, never clipped fragments or other matches
6092
+ on the same line. Mutation batches retain {§edit-batch-merges} and collision
6093
+ handling; a pattern does not create another overlap-recovery algorithm.
6094
+
6095
+ FIND retains locators even without source coordinates. READ may display
6096
+ `enclosingRegion` as context, but a selected value with neither kind of location
6097
+ returns `422 pattern-source-unlocated`. EDIT, COPY, MOVE,
6098
+ and textual KILL require exact `region` evidence for every selected match;
6099
+ enclosing or unlocated values return that same Problem without changing any
6100
+ source or destination. Absence of matches remains an ordinary empty selection.
6101
+
6102
+ A follow-up ```` ```READ (resource) <SL,SC,EL,EC> ```` retrieves the exact
6103
+ region. JSONPath/XPath remain locators and matchers, not a structural update
6104
+ language: `//item` selects the element, `//item/text()` its direct text nodes,
6105
+ and `$.host` the JSON value including its lexical quotes/escapes, never the
6106
+ property name or colon. Literal replacement does not serialize a value or
6107
+ repair neighboring punctuation. Readable projections have their own coordinates.
5981
6108
 
5982
6109
  ### §ext-mimetype Path-extension declares mimetype
5983
6110
 
@@ -6038,7 +6165,7 @@ Auto-derived text mimetypes anywhere in plurnk-service normalize to `text/markdo
6038
6165
  Carried from the contract walk; durable.
6039
6166
 
6040
6167
  - **Dialect/mimetype mismatch** → 415 (xpath on text/plain → 415; jsonpath on JSON-shapeless mimetypes → 204 because outline is empty, not 415).
6041
- - **Binary markers** → 415 for text operations. A readable binary source is durably represented as projected `text/markdown` under {§membership-source-projection}; source-aware File EDIT remains 415.
6168
+ - **Binary coordinates** are numeric byte positions for READ and transfer; text EDIT does not author native bytes ({§binary-parity}). Readable projections retain their own textual coordinates ({§membership-source-projection}).
6042
6169
  - **EDIT `<L>` on non-existent entry** → body becomes content; `<L>` is positional-only on existing content.
6043
6170
  - §copy-l-source-range **COPY/MOVE source scope** selects only the addressed source channel and
6044
6171
  first resolves and, when required, prepares the same canonical
@@ -6059,7 +6186,7 @@ Carried from the contract walk; durable.
6059
6186
  {§copy-move-observation}.
6060
6187
  - **READ rx** prefixes every textual line under {§render-rule-line-navigable-prefix}; eligible
6061
6188
  editable resources carry `@hash N:`, and all others carry `N:`.
6062
- - **FIND pattern** (`[{"pattern": …}]` in the heading, {§matcher-option}) applies to the addressed entry channel (all dialects), per-candidate via the in-tree `Matcher.matchAgainstContent` ({§matcher-dispatch}; status 200 = content hit → entry selected). The target scope and channel select candidates; the path-glob is the (target). On READ, EDIT, KILL, COPY and MOVE the same heading pattern selects lines within one resource ({§read-pattern}, {§edit-pattern}, {§kill-pattern}, {§copy-move-pattern}).
6189
+ - **Pattern selection** applies to the addressed source channel under {§slice-semantics-compose-pattern}. The path and channel select resources; FIND reports matches and READ displays their lines. EDIT, textual KILL, COPY, and MOVE act on exact selected spans, not their enclosing lines ({§read-pattern}, {§edit-pattern}, {§kill-pattern}, {§copy-move-pattern}).
6063
6190
  - **Scoped KILL** on the **log** (`log:///`) removes a body span from its readable projection ({§log-kill-scope}); on an entry it deletes that span through the EDIT path ({§kill-scope-entry}). A whole-entry KILL deletes the entry, or one `#fragment` channel.
6064
6191
  - **File scheme** detects with `Mimetypes.detect({ path })` and classifies with the same configured service ({§mimetype-classification-consumption}). Handler-declared binary sources materialize through {§membership-source-projection}; projected bodies are READ-able, while source-aware EDIT remains 415.
6065
6192
 
@@ -6067,7 +6194,14 @@ Carried from the contract walk; durable.
6067
6194
 
6068
6195
  A KILL with a text-coordinate scope aimed at an entry-bearing scheme deletes exactly that span: core prepares and dispatches it as an EDIT with an empty body over the same marker, so anchors resolve, proposals gate it, and the merge facts and receipt are the EDIT path's — while the log row records the model's KILL. Its packet metadata and canonical log body use {§edit-result-receipt-projection}. ```` ```EDIT (path) <scope> ```` with an empty body remains the same act spelled the other way; the teaching names KILL.
6069
6196
 
6070
- §kill-pattern **A pattern on an entry KILL deletes each matching line.** ```` ```KILL (path) [{"pattern": "beta"}] ```` takes the same EDIT path as a scoped KILL, expanded under {§edit-pattern} in whole lines: the resource is read once, the matcher runs line by line, and every line a match touches becomes one empty-body line splice in one atomic batch guarded by those lines' anchors. A numeric scope bounds the lines the pattern may touch. Zero matches change nothing (204, `matched: 0`); a whole-entry KILL never widens from a pattern that selected nothing. The receipt is the EDIT path's, compacted the same way: `matched` lines, the first deletion's `receipt` with its `removedText` ({§edit-receipt-removed-text}), and `last` for the final one. Node-selecting patterns (`//`, `$`) select whole lines here, as they name nodes with line extents; resource-selecting ones (`~`, `&`) are refused (400 `pattern-dialect-unsupported`, {§pattern-dialect-find-only}). The log stays the exception: a pattern on `log:///` selects rows ({§log-curation-set-selection}), and a stream scheme's KILL is process control, so a pattern there is 400 `kill-pattern-unsupported`.
6197
+ §kill-pattern **A pattern on a textual entry KILL removes exactly its matches.**
6198
+ It is the same empty-body EDIT under {§edit-pattern}, including exact column
6199
+ and multiline regions, scope, preconditions, proposals, and receipt. It has no
6200
+ separate line-deletion mode. Zero matches leave the resource unchanged (204,
6201
+ `matched: 0`); a pattern never becomes whole-resource deletion. The receipt
6202
+ counts selected spans and retains the first and last applied effects.
6203
+ Log KILL still selects retirement rows ({§log-curation-set-selection}); stream
6204
+ KILL controls a process and refuses a content pattern (`kill-pattern-unsupported`).
6071
6205
 
6072
6206
  ---
6073
6207
 
@@ -6087,7 +6221,7 @@ teardown. The runner joins the test body's cleanup before starting the next
6087
6221
  specimen. The shared workspace and story helpers cover setup, inference and
6088
6222
  oracle failures, preserve the primary failure when their cleanup also fails,
6089
6223
  and attempt every registered disposal.
6090
- Provider attempt/recovery limits remain independent; a harness cancellation is
6224
+ The provider whole-call deadline and recovery policy remain independent; a harness cancellation is
6091
6225
  not evidence that the provider's own deadline expired.
6092
6226
 
6093
6227
  §provider-conformance-matrix **Every configured model alias is exercised through a