@plurnk/plurnk-contracts 1.17.0 → 1.19.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 (93) hide show
  1. package/.env.defaults +11 -0
  2. package/README.md +10 -43
  3. package/SPEC.md +363 -280
  4. package/dist/conformance/agui-v1.json +3 -50
  5. package/dist/schema/CapabilityDescriptor.json +6 -1
  6. package/dist/schema/CapabilitySelector.json +6 -1
  7. package/dist/schema/ClientStatement.json +0 -10
  8. package/dist/schema/FunctionalityDefinitionState.json +9 -1
  9. package/dist/schema/LoopPolicy.json +32 -3
  10. package/dist/schema/LoopPolicyRequest.json +16 -0
  11. package/dist/schema/McpConfigurationOverlay.json +1 -14
  12. package/dist/schema/McpServerDefinition.json +1 -1
  13. package/dist/schema/PlurnkStatement.json +30 -41
  14. package/dist/schema/ProposalProjection.json +6 -2
  15. package/dist/schema/SkillDefinition.json +3 -3
  16. package/dist/schema/TextLineMarker.json +1 -1
  17. package/dist/src/ApplicationPort.d.ts +38 -10
  18. package/dist/src/ApplicationPort.d.ts.map +1 -1
  19. package/dist/src/LoopLifecycle.d.ts +5 -0
  20. package/dist/src/LoopLifecycle.d.ts.map +1 -1
  21. package/dist/src/LoopLifecycle.js +15 -0
  22. package/dist/src/LoopLifecycle.js.map +1 -1
  23. package/dist/src/MessageResource.d.ts +30 -0
  24. package/dist/src/MessageResource.d.ts.map +1 -0
  25. package/dist/src/MessageResource.js +2 -0
  26. package/dist/src/MessageResource.js.map +1 -0
  27. package/dist/src/PlurnkParseError.d.ts +1 -3
  28. package/dist/src/PlurnkParseError.d.ts.map +1 -1
  29. package/dist/src/PlurnkParseError.js +1 -4
  30. package/dist/src/PlurnkParseError.js.map +1 -1
  31. package/dist/src/Problems.d.ts +1 -0
  32. package/dist/src/Problems.d.ts.map +1 -1
  33. package/dist/src/Problems.js +19 -1
  34. package/dist/src/Problems.js.map +1 -1
  35. package/dist/src/TurnDisposition.d.ts +3 -4
  36. package/dist/src/TurnDisposition.d.ts.map +1 -1
  37. package/dist/src/TurnDisposition.js +5 -21
  38. package/dist/src/TurnDisposition.js.map +1 -1
  39. package/dist/src/Validator.d.ts +3 -3
  40. package/dist/src/Validator.d.ts.map +1 -1
  41. package/dist/src/Validator.js +14 -15
  42. package/dist/src/Validator.js.map +1 -1
  43. package/dist/src/index.d.ts +3 -7
  44. package/dist/src/index.d.ts.map +1 -1
  45. package/dist/src/index.js +4 -7
  46. package/dist/src/index.js.map +1 -1
  47. package/dist/src/types.d.ts +15 -6
  48. package/dist/src/types.d.ts.map +1 -1
  49. package/dist/src/types.generated.d.ts +60 -76
  50. package/dist/src/types.generated.d.ts.map +1 -1
  51. package/dist/src/types.js +19 -12
  52. package/dist/src/types.js.map +1 -1
  53. package/package.json +7 -20
  54. package/plurnk.md +49 -73
  55. package/bin/plurnk-contracts.js +0 -43
  56. package/dist/schema/AcpPlan.json +0 -63
  57. package/dist/schema/Plan.json +0 -65
  58. package/dist/src/AcpPlanValue.d.ts +0 -6
  59. package/dist/src/AcpPlanValue.d.ts.map +0 -1
  60. package/dist/src/AcpPlanValue.js +0 -36
  61. package/dist/src/AcpPlanValue.js.map +0 -1
  62. package/dist/src/AstBuilder.d.ts +0 -21
  63. package/dist/src/AstBuilder.d.ts.map +0 -1
  64. package/dist/src/AstBuilder.js +0 -876
  65. package/dist/src/AstBuilder.js.map +0 -1
  66. package/dist/src/PlanValue.d.ts +0 -9
  67. package/dist/src/PlanValue.d.ts.map +0 -1
  68. package/dist/src/PlanValue.js +0 -63
  69. package/dist/src/PlanValue.js.map +0 -1
  70. package/dist/src/PlurnkErrorStrategy.d.ts +0 -11
  71. package/dist/src/PlurnkErrorStrategy.d.ts.map +0 -1
  72. package/dist/src/PlurnkErrorStrategy.js +0 -253
  73. package/dist/src/PlurnkErrorStrategy.js.map +0 -1
  74. package/dist/src/PlurnkParser.d.ts +0 -15
  75. package/dist/src/PlurnkParser.d.ts.map +0 -1
  76. package/dist/src/PlurnkParser.js +0 -330
  77. package/dist/src/PlurnkParser.js.map +0 -1
  78. package/dist/src/RecordingListener.d.ts +0 -9
  79. package/dist/src/RecordingListener.d.ts.map +0 -1
  80. package/dist/src/RecordingListener.js +0 -37
  81. package/dist/src/RecordingListener.js.map +0 -1
  82. package/dist/src/generated/plurnkLexer.d.ts +0 -162
  83. package/dist/src/generated/plurnkLexer.d.ts.map +0 -1
  84. package/dist/src/generated/plurnkLexer.js +0 -1060
  85. package/dist/src/generated/plurnkLexer.js.map +0 -1
  86. package/dist/src/generated/plurnkParser.d.ts +0 -436
  87. package/dist/src/generated/plurnkParser.d.ts.map +0 -1
  88. package/dist/src/generated/plurnkParser.js +0 -2972
  89. package/dist/src/generated/plurnkParser.js.map +0 -1
  90. package/dist/src/generated/plurnkParserVisitor.d.ts +0 -263
  91. package/dist/src/generated/plurnkParserVisitor.d.ts.map +0 -1
  92. package/dist/src/generated/plurnkParserVisitor.js +0 -227
  93. package/dist/src/generated/plurnkParserVisitor.js.map +0 -1
package/SPEC.md CHANGED
@@ -2,14 +2,15 @@
2
2
 
3
3
  ## 1. Overview
4
4
 
5
- §contract-authority This package is the single authority for PLURNK's language, schemas, generated
6
- types, parser, and runtime-neutral wire envelopes. Its package root
7
- is the single code API for those contracts.
5
+ This package is the single authority for PLURNK's language, schemas, generated types and
6
+ runtime-neutral wire envelopes; `@plurnk/plurnk-parser` implements the language it specifies
7
+ ({§parser-consumers}). Its package root is the single code API for those contracts
8
+ ({§root-value-api}).
8
9
 
9
10
  | Surface | Canonical export or artifact |
10
11
  | ------------------------------------------------------------------------------- | --------------------------------------------------- |
11
- | Parser, AST, validators, Problems, results, Notices, text regions and extents | `@plurnk/plurnk-contracts` |
12
- | Capability and loop policies with their defaults | `CapabilityPolicy`, `LoopPolicy`, `DEFAULT_CAPABILITY_POLICY`, `DEFAULT_LOOP_POLICY` |
12
+ | AST, validators, Problems, results, Notices, text regions and extents | `@plurnk/plurnk-contracts` |
13
+ | Capability and loop policies | `CapabilityPolicy`, `LoopPolicy`, `LoopPolicyRequest`, `PROPOSAL_POLICIES` |
13
14
  | Durable reasoning intent | `ReasoningPolicy`, `REASONING_POLICIES` |
14
15
  | Model route and catalog discovery | `ModelRoute`, `ModelCatalogQuery`, `ModelCatalogPage`, `ModelReadiness` |
15
16
  | Stopped-world client contract | `ProposalDisposition`, `ProposalProjection` |
@@ -89,12 +90,10 @@ and previews; never reformat literal resources, JSONL framing, or wire/evidence
89
90
  serialization. Compact aggregate rows ({§json-result-rendering}) and packet
90
91
  metadata retain their deliberate layouts.
91
92
 
92
- ## §contract-layers 1.1 Contract layers and admission boundary
93
+ ## 1.1 Contract layers and admission boundary
93
94
 
94
- PLURNK uses one contract with deliberately different projections. A tolerant
95
- ingester accepting a spelling does not make that spelling canonical model
96
- teaching, and an operator's sampling grammar admitting a sentence does not
97
- make its runtime semantics valid.
95
+ One contract, deliberately different projections (ARCHITECTURE.md). Each layer
96
+ below owns what it alone can decide.
98
97
 
99
98
  ```mermaid
100
99
  flowchart LR
@@ -127,7 +126,7 @@ WHATWG `URL`, ECMAScript `RegExp`, XPath 1.0, and RFC 9535 JSONPath parsers.
127
126
  The runtime owner decides facts that require state or operation-specific
128
127
  meaning, including registered scheme resolution, target existence, tag
129
128
  selection, text-region bounds, result ordering, full-text ranking, mutation
130
- effects, executor behavior, and numeric operation-code semantics.
129
+ effects, and executor behavior.
131
130
 
132
131
  ### §contract-proposal-projection Loop policy and stopped-world projection
133
132
 
@@ -168,11 +167,15 @@ workspace layer and their normalized intersection: `service`, `workspace`, and
168
167
  capability policy or inherited bound; every actor uses the same live workspace
169
168
  policy. A client never derives effective authority from the mutable layer alone.
170
169
 
171
- §loop-policy `DEFAULT_CAPABILITY_POLICY` and `DEFAULT_LOOP_POLICY` are the
172
- contracts-owned complete defaults. A loop policy is immutable after creation;
173
- its `proposals` field chooses one downstream settlement posture, independently
174
- of workspace capability policy. Capability
175
- admission precedes effect classification and proposal settlement.
170
+ §loop-policy A `LoopPolicy` is complete and immutable after creation:
171
+ `proposals` chooses one downstream settlement posture and `attended` says
172
+ whether anyone can answer, independently of workspace capability policy. An
173
+ unattended loop cannot hold a proposal for review, so the schema refuses that
174
+ pair. A `LoopPolicyRequest` is the part of a policy its creator chose to state.
175
+ Contracts hold no default for the rest: the daemon's panel supplies it
176
+ ({§loop-policy-composition}). `PROPOSAL_POLICIES` is the schema-owned
177
+ vocabulary of `proposals`. Capability admission precedes effect
178
+ classification and proposal settlement.
176
179
 
177
180
  §reasoning-policy-wire `ReasoningPolicy` is exactly `off | adaptive | low |
178
181
  medium | high`. The schema owns this shared wire vocabulary. Providers own the
@@ -255,7 +258,8 @@ and parse diagnostics are separate contracts.
255
258
  body
256
259
  ```
257
260
 
258
- ```OP (path)? <scope>?```
261
+ ```OP (path)? <scope>?
262
+ ```
259
263
 
260
264
  ```executor (program-or-tool)?
261
265
  input
@@ -263,37 +267,56 @@ input
263
267
  `````
264
268
 
265
269
  §section-boundary Every statement is one backtick block. Its header occupies one
266
- physical line: a fence of at least three backticks, an optional numeric delimiter
270
+ physical line: a fence of at least four backticks ({§four-backtick-operations}), an optional numeric delimiter
267
271
  ({§numeric-delimiter}), then the name and its slots. A closer is shown by
268
272
  convention and never demanded ({§fence-closer}, {§closer-fallback}). There are no
269
- operation suffixes or heading levels. Nothing in the language is counted by the
270
- author: every boundary is an anchored line the parser recognizes by its first
271
- characters (operator, 2026-09-12: counted pairs failed 52 of 363 turns on
272
- GLM-5.3-flash; anchored tokens failed none).
273
+ operation suffixes or heading levels. Complete nested matches take precedence
274
+ over local recovery ({§balanced-fences}).
273
275
 
274
276
  §fence-closer A block opened with N backticks and delimiter D (its digits, possibly
275
- none) closes at the first line at column zero made of at least N backticks,
277
+ none) closes at the first unclaimed line made of at least N backticks,
276
278
  exactly D, and nothing else but horizontal whitespace. Count follows CommonMark: a
277
279
  shorter fence inside the body is body; an equal or longer bare fence closes a bare
278
- block. The delimiter compares exactly: a bare fence never closes a delimited block,
280
+ block unless it closes a balanced nested block ({§balanced-fences}). The delimiter
281
+ compares exactly: a bare fence never closes a delimited block,
279
282
  and a delimited fence never closes a bare one. The compact one-line form closes on
280
283
  its heading line after the modifiers under the same rule.
281
284
 
285
+ §balanced-fences A complete nested interpretation takes precedence over missing-closer
286
+ recovery. Within an undelimited body, line-leading labeled fences open literal
287
+ blocks; their matching closers close the innermost block first. The enclosing
288
+ block and its nested blocks must all close, with matching widths and delimiters
289
+ under {§fence-closer}. Equal opener/closer totals alone are insufficient. A complete
290
+ inline block is already closed; a numerically delimited block is opaque until its
291
+ own closer. Preserve every nested body byte, including apparent OPs and known
292
+ executors, without dispatching them. If no complete enclosing interpretation exists,
293
+ retain {§fence-heading-in-body} and {§closer-fallback}. These rules apply equally to
294
+ model programs, stored programs, client operations, and reasoning quotations.
295
+
282
296
  §numeric-delimiter Digits between the opening backticks and the name (an opener
283
- carrying `42EDIT (x)`) identify the block, and only a fence carrying `42` closes it. This is how a
284
- block nests fences of its own width: with a delimiter, a body may carry bare fences
285
- and headings of the same count. The delimiter is syntax, never AST or persistence
297
+ carrying `42EDIT (x)`) identify the block, and only a fence carrying `42` closes it.
298
+ This explicitly protects bare fences and headings, including incomplete examples.
299
+ The delimiter is syntax, never AST or persistence
286
300
  state; `PlurnkParser.frame` chooses one when the body it wraps holds a heading line
287
301
  of four or more backticks ({§statement-rendering}).
288
302
 
289
- §fence-heading-in-body A fence line of four or more backticks, optional digits, and
290
- a name that is a native operation or a known executor is a heading wherever it
291
- stands. Inside an open block it ends that block without closing it
303
+ §four-backtick-operations **An operation opens with four backticks.** A heading is a fence of
304
+ four or more backticks; a three-backtick fence is markdown wherever it stands, so an answer's
305
+ code blocks (```` ```sh ````, ```` ```ts ````) are prose and never run (operator,
306
+ 2026-09-18, #761). A three-backtick fence naming an operation or known executor draws one warning that it
307
+ needs four backticks; any other three-backtick fence draws none. `plurnk.md` teaches exactly
308
+ four; longer fences are tolerated, not taught. Every producer of a statement — the parser, a
309
+ client composing `/look`, a client's tab-completion — writes `PLURNK_FENCE` rather than its own
310
+ literal, so no surface can ship a width the parser will quote (plurnk/plurnk#92).
311
+
312
+ §fence-heading-in-body Outside a complete nested block ({§balanced-fences}), a fence
313
+ line of four or more backticks, optional digits, and a name that is a native operation
314
+ or a known executor is a heading. Inside an open block it ends that block without closing it
292
315
  ({§closer-fallback}) and opens the next statement. Fence lines of fewer than four
293
- backticks are headings only outside any block. Known executors are `sh` plus what
316
+ backticks are never headings ({§four-backtick-operations}). Known executors are `sh` plus what
294
317
  the host names in `ParseOptions.executors`. Consequences: a closer glued to the next
295
- opener (eight backticks then `READ`) can never swallow the rest of a turn, and a quoted
296
- heading of four or more backticks inside a body needs the numeric delimiter to stay body.
318
+ opener (eight backticks then `READ`) can never swallow an unbalanced turn, and a numeric
319
+ delimiter preserves quoted headings even when their own fences are incomplete.
297
320
 
298
321
  §closer-fallback A block that ends at a heading or at the end of the input has no
299
322
  closer of its own. Its body is cut back to its last bare fence line (any count,
@@ -302,48 +325,67 @@ ending goes with it; when no bare fence line exists the body is the whole span l
302
325
  one terminating line ending. This carries no diagnostic: a missing closer is never
303
326
  an admission failure, and {§unparsed-tail-boundary} is not involved.
304
327
 
305
- §fence-boundary Inside a body, fences are read by count and delimiter, never by
306
- name, except for the heading rule above:
328
+ §fence-boundary Balanced nesting is resolved before local recovery. Otherwise,
329
+ fences are read by count and delimiter, except for the heading rule above:
307
330
 
308
331
  | Fence encountered inside a body | Meaning |
309
332
  |---|---|
333
+ | Part of a complete nested block | Literal body, including its openers and closers |
310
334
  | Fewer backticks than the block's own | Body |
311
335
  | At least the block's backticks, bare, block undelimited | The block's closer |
312
336
  | At least the block's backticks carrying the block's delimiter | The block's closer |
313
337
  | At least the block's backticks with any other delimiter | Body |
314
338
  | Four or more backticks naming a native operation or known executor | A heading: ends the block, opens the next statement |
315
339
 
316
- §indented-fences Leading horizontal whitespace before an opener or a closer is
317
- not part of the fence: an indented fence line is a fence line, on openers,
318
- closers, headings that end a block, and the closer fallback. A body keeps its own
319
- lines' indentation. CommonMark allows three spaces; this allows any, because a
320
- model that indents an emission indents all of it (operator, 2026-09-12: measured
321
- at five to ten percent of emissions on GLM-5.3-flash).
340
+ §indented-fences Leading horizontal whitespace before a CLOSER is not part of the fence: an
341
+ indented closer, heading-that-ends-a-block, or closer fallback still closes, and a body keeps its
342
+ own lines' indentation. An OPENER is different: an operation's backticks follow a newline
343
+ directly (operator, 2026-09-18), so an indented fence opens a quotation, never an operation —
344
+ CommonMark reads an indented block as code, and `plurnk.md` shows its own examples that way. This
345
+ reverses the 2026-09-12 tolerance (then measured at five to ten percent of emissions on
346
+ GLM-5.3-flash; 2.8% of that lane's emissions today). The cost is paid loudly: an indented fence
347
+ naming a known operation draws `must start its line to run` and is an operation attempt, never an
348
+ answer ({§prose-conclusion}), so the loop continues instead of delivering a program as prose.
322
349
 
323
350
  §inline-chain A closer on a heading line, or on a body's closing line, may be
324
351
  followed on that same line by the next opener; the closer still closes, and the
325
352
  opener opens. This absorbs the habit of writing several operations in one
326
- paragraph after prose. Prose after a closer on its line ends the chain.
353
+ paragraph after prose. Prose after a closer on its line ends the chain; slot-shaped
354
+ text there is the heading's own and is read under {§transparent-inline-closer}.
355
+
356
+ §transparent-inline-closer **A closer mid-heading is read as if it were not written.** A
357
+ closing fence on a heading line followed by more of that heading — a `<scope>`, an
358
+ `[option block]`, a `<!-- aside -->`, or a naked matcher — does not end the reading: the
359
+ heading keeps taking its slots under {§trailing-slots} and {§naked-pattern}, exactly as though
360
+ the closer were absent, so ````` ````READ (a.md)```` ````` `<1,2>` is the same operation as `READ (a.md) <1,2>`.
361
+ The closer is still a closer: the block ends with that physical line and never reaches down for
362
+ the next operation, which is what a bare heading carrying a matcher would do. A closer followed
363
+ by the next opener is {§inline-chain}, and by nothing is the ordinary {§fence-closer}. There is
364
+ no ambiguity to resolve: a slot begins with `<` or `[` and an opener with a backtick run, so the
365
+ shapes are disjoint (operator, 2026-09-13: "If there is no risk of ambiguity, then we add
366
+ tolerance. Turning model soup into operations instead of errors is a cardinal imperative").
327
367
 
328
368
  §executor-case **An executor tag in any case.** A fence tag that matches a
329
369
  registered executor's name case-insensitively opens that executor (`SH` opens
330
370
  `sh`), and the statement's `executor` is the registered spelling, so a lookup
331
371
  by that name never misses. Operation names stay uppercase by teaching and were
332
372
  never written otherwise in 12,000 fences; one `SH` in 1,300 `sh` fences was
333
- (2026-09-13 census). An unregistered name in any case is still prose
334
- ({§interstitial-fence}).
335
-
336
- §one-line-turn **A whole turn on one line.** The most frequent private rejection
337
- across the 2026-09-12/13 dumbox runs (five of eleven) was a turn emitted as a
338
- single line: prose, then heading after heading with no line ending anywhere. Two
373
+ (2026-09-13 census). An unregistered name without an accepted spelling
374
+ ({§executor-js-spelling}) is still prose ({§interstitial-fence}).
375
+
376
+ §executor-js-spelling When `node` is registered and `js` is not, `js` names
377
+ `node` under {§executor-case}. Parsing normalizes the runtime before admission
378
+ and dispatch; execution, policy, receipts, and output addresses remain Node's.
379
+ The authored source remains unchanged. This spelling adds no executor, discovery
380
+ entry, or model-facing teaching. An explicitly registered `js` retains its own
381
+ identity; absent `node`, the shorthand grants no executable capability.
382
+
383
+ §one-line-turn **A whole turn on one line.** A model may emit a turn as a single
384
+ line: prose, then heading after heading with no line ending anywhere. Two
339
385
  rules absorb it. The next opener on a heading's own line, after the heading's
340
386
  slots, ends that heading's block bodyless and opens ({§empty-section}), so
341
387
  `````EDIT (a.rs) <60,66> <!-- drop --> ````EDIT (b.rs) <31,38>` is two scoped
342
- deletions; and a TASK whose inventory rides its heading line as a `[…]` block
343
- (`````TASK [{"content": "…", "status": "in_progress"}] ````) takes that block as
344
- its body when nothing sits beneath the heading, with one warning-severity
345
- advisory naming the body as where the inventory belongs. A block beneath the
346
- heading still wins.
388
+ deletions. Literal inline bodies follow {§heading-inline-body}.
347
389
 
348
390
  §anchor-digits In a text scope, `@` followed by one to four digits cannot be a
349
391
  hash and is read as that line number, with one warning-severity advisory naming
@@ -353,33 +395,81 @@ the five-character anchor form. Five characters after `@` are always an anchor.
353
395
  line takes the rest of the line as the aside, with one warning-severity advisory.
354
396
  A closed aside followed by more text is unchanged.
355
397
 
356
- §interstitial-fence A fence line that names no native operation and no known
357
- executor opens nothing: unlabeled, or tagged like a code block (`ts`, `json`),
358
- outside a block it is prose and ignored like every other outside line
359
- ({§whitespace-contract}); inside a body it is body. Nothing is promoted into a
360
- header or recursively parsed. There is no implicit SEND: a reply is an explicit
361
- `SEND` block. (This replaces the retired unlabeled-fence SEND of the fences
362
- chapter, whose unlabeled fences turned displaced headings into silent messages.)
398
+ §quotation **A fence that opens no operation quotes.** Outside a body, a line-start fence that
399
+ is not an operation heading — unlabeled, tagged like a code block (`ts`, `json`), three
400
+ backticks ({§four-backtick-operations}), indented, or four or more with an unknown name — opens a
401
+ quotation that runs to its matching closer (same character, width at least the opener's) or to
402
+ the end of the input. Everything inside is data: no operation runs there, native tool-call
403
+ markup is not read ({§native-tool-calls}), and no heading draws an advisory. So a model may show
404
+ plurnk's own operations in an answer. Three exceptions keep programs whole:
405
+ CommonMark's own rule that a backtick opener's line carries no further backtick, so
406
+ ```` ```READ (x)``` ```` is inline code and quotes nothing after it; a bare fence directly under a
407
+ line carrying a fence run, which is an orphaned closer and quotes nothing; and a tag that is a
408
+ missed operation — a known name under four backticks or off column zero, or an unknown name at
409
+ operation width — which still draws one warning (`markdown` and `md` are polite envelopes and
410
+ draw none). There is no implicit SEND: a reply is prose ({§prose-conclusion}) or an explicit
411
+ `SEND` block. Origin (#767, 2026-09-18): under prose answers, a quoted example executed.
412
+
413
+ §interstitial-fence Superseded by {§quotation}: an unlabeled fence no longer opens nothing, it
414
+ quotes. (It in turn replaced the retired unlabeled-fence SEND of the fences chapter, whose
415
+ unlabeled fences turned displaced headings into silent messages.)
416
+
417
+ §closer-aside A closing fence followed on its line by one aside and nothing else
418
+ is the closer; the aside is outside text. Read as body, that line would be written
419
+ into the edited resource (#758: a recorded EDIT deleting a line wrote
420
+ ```` ```` <!-- remove duplicated Result import --> ```` into a Python file). A
421
+ closing fence followed by any other text is still body.
422
+
423
+ §heading-slot-order A heading near-miss with exactly one reading is read as that
424
+ reading, with no diagnostic and no teaching (#758):
425
+
426
+ - an aside written before the heading's remaining scope or JSON option block is
427
+ read after them (`READ (a.md) <!-- why --> <1,-1>`); an aside followed by a
428
+ target or a non-JSON block keeps its place and stays refused;
429
+ - zero-width characters (U+200B–U+200D, U+2060, U+FEFF) on a heading line are skipped;
430
+ - a matcher that begins with a sigil and is quoted in single backticks
431
+ (`` `^def test_` ``) is that matcher; quoted text without a sigil stays refused.
432
+
433
+ Tokens keep their source positions; only their order in the stream changes.
434
+
435
+ §native-tool-calls An emission that yields no operation may be a model's native
436
+ tool-call markup (DeepSeek's `<||DSML|| calls>` block) naming a plurnk operation or
437
+ a known executor. Each `invoke` is read as that operation's canonical fence:
438
+ `path`/`target` fill the target, `scope`/`range`/`lines` the scope, `pattern` a
439
+ matcher option, `aside` the aside, and `body`/`content`/`command` or plain lines
440
+ inside the invoke the body; plurnk slots written after the invoke name are kept.
441
+ Each block keeps its line count, so statement positions still name the source
442
+ line. An invoke with an unknown name or parameter leaves the whole emission as it
443
+ was. An emission that already yields an operation is never rewritten. No
444
+ diagnostic, notice or teaching mentions the reading (#760).
445
+
446
+ §operation-attempt An emission with no operation is either prose, which a host may take
447
+ as the model's answer, or an operation attempt. `PlurnkParser.operationAttempt(input,
448
+ executors)` names the attempt: a line opening a four-backtick fence, a heading outside
449
+ any fence ({§bare-heading-advisory}; a known executor's name is a heading only when a
450
+ slot follows it), native tool-call markup that {§native-tool-calls} did not read, or
451
+ echoed packet rows (`### log://…`). Anything else, three-backtick code blocks included,
452
+ is prose (#761).
363
453
 
364
454
  §bare-heading-advisory An operation name that opens a line outside any block in the
365
- shape of a heading (`READ (…)`, `TASK`, …) is prose and runs nothing. The parser
455
+ shape of a heading (`READ (…)`, `NOTE`, …) is prose and runs nothing. The parser
366
456
  emits one warning-severity advisory naming the fence form, placed after the parsed
367
457
  items, so the loss is never quiet.
368
458
 
369
459
  §empty-section Both the compact bodyless form and an empty multiline block
370
- normalize optional bodies to null. TASK normalizes an empty body to `[]`
371
- under {§plan-value}. Closing fences are conventional, never required
460
+ normalize optional bodies to null. Closing fences are conventional, never required
372
461
  ({§closer-fallback}).
373
462
 
374
463
  §statement-rendering `PlurnkParser.stringify` renders native OP names and named
375
- EXEC executors from the shared AST, with one blank line between operations.
464
+ runtime fences from the shared AST, with one blank line between operations.
376
465
  Every closing fence occupies its own line, including bodyless operations;
377
466
  inline fences remain accepted input, not generated examples.
378
467
  It chooses at least four backticks and more than any run within the body, and a
379
468
  numeric delimiter whenever the body holds a heading line of four or more backticks
380
469
  ({§fence-heading-in-body}), preserving body bytes on reparse. Fence length and
381
470
  delimiter are syntax, not AST or persistence state. Core-authored programs use
382
- this serializer and the ordinary admission parser.
471
+ this serializer and the ordinary admission parser. Rendering preserves a matcher
472
+ beside owner metadata, not only a matcher carried inside its `pattern` option.
383
473
 
384
474
  | Element | Contract |
385
475
  |---|---|
@@ -398,8 +488,9 @@ adjacent slots and scope/metadata permutations within a selection without
398
488
  changing ownership or making them distinct canonical forms. Each selection
399
489
  has at most one scope; its metadata blocks retain their authored order.
400
490
 
401
- §plan-slotless TASK accepts no target or metadata. Its optional scope carries
402
- waiting timing; its inventory body begins below the header.
491
+ §lifecycle-slots NOTE accepts no target, scope, or metadata. WAIT retains its
492
+ optional target and ignores syntactically valid scope and metadata without diagnostics
493
+ ({§send-wait-scope}). Their literal bodies begin below the header.
403
494
 
404
495
  §heading-inline-body Nonempty body text belongs below the fence header.
405
496
  The ingester tolerates body text after horizontal whitespace on the header,
@@ -413,7 +504,7 @@ routing, timing, or body input. Comments inside a body remain literal except
413
504
  for the narrowly owned {§misplaced-aside-advisory}.
414
505
 
415
506
  §scheme-metadata-modifier A target may carry one single-line `[metadata]`
416
- block after its scope; an executor fence also admits it without a target.
507
+ block after its scope; executor and SEND fences also admit it without a target.
417
508
  Read with its brackets, the block is a JSON array of option objects, merged
418
509
  left to right with later keys winning; the keys belong to the selected scheme
419
510
  or executor, which owns interpretation, validation and authority. The language
@@ -422,8 +513,10 @@ balanced brackets inside the block are retained, and double-quoted strings
422
513
  protect their brackets. Brackets inside `(path)` remain ordinary path and
423
514
  glob characters. A block that is not valid JSON, or a second block on one
424
515
  operand, is the owner's `400`, never a parser diagnostic. An unfinished block
425
- or multiline metadata loses its boundary. One key is the language's own:
426
- `pattern` ({§matcher-option}).
516
+ or multiline metadata loses its boundary. Two keys never reach an owner:
517
+ `pattern`, the language's own ({§matcher-option}), and `env`, reserved for the
518
+ service's environment option on the operations that open a process or a
519
+ Worker; the shared reader withholds both from the owner's options.
427
520
 
428
521
  §matcher-option **`pattern` is the matcher, and it lives in the heading.** On
429
522
  FIND, READ, KILL, EDIT, and each COPY/MOVE operand, the option
@@ -453,7 +546,7 @@ whose block left no metadata back bare when the bare form reads back identically
453
546
 
454
547
  | Element | Shape or role |
455
548
  |---|---|
456
- | Native OP | `FIND READ EDIT COPY MOVE SEND EXEC BARE WORK FORK KILL TASK` |
549
+ | Native OP | `FIND READ EDIT COPY MOVE SEND BARE WORK FORK KILL NOTE WAIT` |
457
550
  | Executor name | Letters, digits, `_`, `.`, `+`, or `-`; reserved OPs win |
458
551
  | Fence | Three or more backticks, matched by exact count |
459
552
  | `(path)` | Local path, URI, program or tool name; §5 |
@@ -473,90 +566,68 @@ governed by {§canonical-statement}; runtime conditions remain explicit below.
473
566
  | EDIT | required file or entry | required for an existing target | literal text |
474
567
  | COPY | required source and destination | optional region after each path | empty |
475
568
  | MOVE | required source and destination | optional region after each path | empty |
476
- | EXEC | fence names executor; optional program/tool path ({§exec-executor-slot}) | optional timeout, poll | optional program input |
569
+ | execution | the fence name is the runtime; optional program/tool path ({§exec-executor-slot}) | optional timeout, poll | optional program input |
477
570
  | BARE | optional prompt resource | none | prompt; optional with a path |
478
571
  | WORK | optional fresh `worker://name`, or a prompt resource ({§worker-spawn-prompt-resource}) | none | prompt; optional with a resource |
479
572
  | FORK | optional context-inheriting `worker://name`, or a prompt resource | none | prompt; optional with a resource |
480
573
  | KILL | required target, including a log item | optional text region ({§kill-scope}) | none; the matcher is the `pattern` option |
481
- | SEND | optional recipient | optional recipient timing | message |
482
- | TASK | none | optional timeout and poll for waiting intent | Plurnk Plan JSON array |
483
-
484
- §operation-code-polymorphism Operation-result statuses and turn dispositions are
485
- distinct facts. TASK derives lifecycle intent from its inventory;
486
- SEND and KILL carry no disposition operand.
487
-
488
- §plan-value **TASK carries the complete current task inventory.** Admission
489
- parses one JSON array in any whitespace layout, including
490
- {§json-result-rendering}, strips unknown entry keys, and validates string
491
- `content` and native `status`. Opaque `_meta` remains optional. Nonempty plain
492
- text, malformed JSON, or an invalid array becomes one `in_progress` entry
493
- containing the exact body, with one factual warning. No partial repair or list
494
- inference occurs. A blank body becomes `[]`, never inferred completion.
495
- The normalized array is the sole semantic value in AST, persistence and model
496
- log; exact authored bytes remain in `turnOps`. Earlier inventories are history,
497
- not accumulated obligations. Task descriptions are not executable dependencies.
498
-
499
- §task-inventory-intent The first matching row determines intent, independently
500
- of entry order. Actual execution adjudicates intent under {§wait-obligation-matrix}.
501
-
502
- | Inventory condition | Intent | Derived lifecycle status |
503
- |---|---|---|
504
- | TASK omitted | Continue silently | 102 |
505
- | Explicit empty inventory | Recover empty inventory | 102 |
506
- | Any `in_progress` | Continue independent actionable work | 102 |
507
- | Any `waiting`, no `in_progress` | Await work or an event | 202 |
508
- | Any `pending`, no actionable or waiting entry | Review blocked dependencies | 102 |
509
- | All terminal, any `completed` | End successfully | 200 |
510
- | All `failed`, nonempty | End unsuccessfully | 499 |
511
-
512
- `pending` is blocked on another task; `in_progress` can be actively advanced;
513
- `waiting` awaits an ongoing stream, worker or external event. `completed` is
514
- successful resolution; `failed` is unsuccessful resolution. A failed sibling
515
- does not terminate independent unfinished work. The engine does not infer a
516
- dependency graph from task text.
517
-
518
- §plan-acp-projection **Only an ACP-facing boundary projects the model-native
519
- Plan.** It constructs ACP's `{ "entries": [...] }` Plan object from the internal
520
- array, synthesizes the ACP-required neutral `medium` priority on every entry
521
- (the model-native Plan carries none). The internal value is never mutated.
522
- Native `waiting` maps to ACP `in_progress`, with `Waiting:` and a space prepended
523
- to its display content; native `failed` maps to ACP `completed`, with `Failed:` and a space.
524
- Both carry their native status in `_meta["plurnk.xyz/status"]`, an edge-owned
525
- key derived from the native status rather than trusted from authored metadata.
526
- Other statuses and unrelated metadata remain unchanged. The labels preserve
527
- meaning even when a generic client ignores extension metadata.
528
- The projected value validates against the separately owned ACP Plan schema pinned
529
- to ACP v1
530
- [`schema-v1.21.0`](https://github.com/agentclientprotocol/agent-client-protocol/tree/schema-v1.21.0)
531
- commit `272bf799f35a258c6a4107a0410ed361e83683d3`.
574
+ | SEND | optional recipient | recipient-defined; none for workers ({§send-directed-scope}) | message |
575
+ | NOTE | none | none | literal working memory |
576
+ | WAIT | optional event source ({§send-wait-scope}) | ignored | explanation of the wait |
577
+
578
+ §note-value NOTE retains its literal body as ordinary model-owned working memory.
579
+ It has no target, scope, metadata, or lifecycle effect. Its full body participates
580
+ in ordinary log token accounting and model-driven curation; prior notes are not
581
+ automatically hidden. A NOTE-only turn is subject to ordinary conclusion,
582
+ repetition and strike rules.
583
+
584
+ §reasoning-notes NOTE is the only operation admitted from exposed provider
585
+ reasoning. The shared fence parser selects line-leading NOTE statements in a
586
+ quotation-preserving reasoning context: other backtick or tilde code blocks are
587
+ opaque, and operation-heading recovery cannot escape them. Blockquoted and inline
588
+ examples are not headings. Program parsing is unchanged; other reasoned operations
589
+ never execute.
590
+ Selected notes precede the content program in the admitted turn and use the
591
+ ordinary dispatcher, persistence and log projection. Reasoning bytes and content
592
+ bytes remain separate, unchanged forensic sources. Rejected or superseded
593
+ provider attempts cannot commit notes. Non-thinking models use NOTE in their
594
+ ordinary program. No task inventory is inferred from notes or lifecycle prose.
532
595
 
533
596
  §exec-executor-slot The fence name selects the executor directly: for example,
534
597
  `python3 (tools/report.py)` or `gitea (issue_list)` on the opening fence line.
535
- Reserved native OP names take precedence. Other names lower to the same EXEC
536
- AST with `executor`, `target`, metadata, timing and body fields.
598
+ Reserved native OP names take precedence. Any other name is an executor fence: its
599
+ AST carries the `runtime` tag and no operation keyword, then `target`, metadata, timing and body fields.
537
600
  Registration is checked by the runtime, not by the syntax parser. An attached
538
601
  MCP service uses that executor path and its owner validates the named tool and
539
602
  input-body JSON against its schema. Unknown names do not fall back to a shell.
540
- The native `EXEC` form without a selected executor retains the runtime's
541
- default executor contract; canonical shell examples name `sh` explicitly.
603
+ There is no runtime-less form: every execution names its runtime, and canonical
604
+ shell examples name `sh` explicitly.
542
605
  The path names a program or tool and is never split. Metadata such as
543
606
  `[{"cwd": "…"}]` remains interpreted by the selected executor.
544
607
 
545
- §turn-disposition TASK is the sole workflow declaration. `TurnDisposition`
546
- derives intent from its canonical inventory under {§task-inventory-intent}.
547
- The AST has no independently settable lifecycle status, target or metadata.
548
- SEND deliberately messages its recipient, or the user when targetless; it
549
- neither changes task status nor terminates a run. A program contains one final
550
- TASK, not last-wins competing inventories. Former lifecycle names are not aliases.
551
-
552
- §send-wait-scope TASK accepts `<timeout[,poll]>` in whole minutes. It applies
553
- only to a waiting intent ({§park-202-only}); the dispatcher validates its bounds.
554
- Otherwise it is unused, with a factual warning rather than a changed outcome.
555
-
556
- §send-directed-scope A recipient SEND preserves an optional numeric scope after
557
- its target and metadata. The addressed owner assigns its semantics; worker
558
- actors use `<delay[,interval]>` ({§worker-scheduled-send}). A targetless message
559
- takes no scope. Scheduling does not change the message body or disposition.
608
+ §turn-disposition WAIT requests parking; its literal body does not control
609
+ scheduling, and its optional target is the label the row keeps, never a join;
610
+ the AST has no independently settable lifecycle status or metadata.
611
+ A turn admits any number of WAITs, all deferred until its other operations
612
+ settle and together one park. End-of-program adjudication owns continuation, joining and completion
613
+ under {§wait-obligation-matrix}; no terminal verb or synthetic receipt is required.
614
+ SEND delivers messages and NOTE retains memory, neither declaring an outcome.
615
+
616
+ §send-wait-scope WAIT's optional target is retained as the row's label; no
617
+ scheme handler runs for it, so every WAIT is the bare park. Scope and metadata
618
+ are discarded without diagnostics,
619
+ including structured scopes. The body, aside, and exact submitted program
620
+ remain intact. WAIT neither creates a schedule nor restricts which ordinary
621
+ events may awaken the loop. A scope slot's content is skipped unread whatever it
622
+ holds (`<sh:///…>` included; #756). Ordinary malformed-header rules still apply;
623
+ a second WAIT is one more label on the same park, never a refusal.
624
+
625
+ §send-directed-scope A recipient SEND carries an optional numeric scope after
626
+ its target and metadata through to the addressed owner, which assigns its
627
+ semantics or refuses it; worker actors refuse one (`scope-unsupported`, 400),
628
+ because later and recurring delivery belong to the schedule family. A
629
+ targetless message takes no scope. A scope never changes the message body or
630
+ disposition.
560
631
 
561
632
  §kill-scope KILL takes an optional text-coordinate scope beside its target, numeric or
562
633
  anchored (```` ```KILL (log:///**/READ) <17,-1>``` ```` or
@@ -575,7 +646,7 @@ and is refused there; a bracket before the target of a non-executor OP is one
575
646
  bounded header diagnostic that selects nothing.
576
647
 
577
648
  The `<scope>` slot is optional where admitted and its domain is OP-specific. FIND
578
- scopes ordered results. EXEC and SEND scope owner-defined timing. READ, EDIT, COPY,
649
+ scopes ordered results. Executions and SEND scope owner-defined timing. READ, EDIT, COPY,
579
650
  MOVE, and KILL use one universal text algebra independent of mimetype; a log
580
651
  KILL admits only its one- and two-line forms for canonical log-body visibility:
581
652
 
@@ -621,8 +692,8 @@ parses as `{ kind: "local", raw: "data/users.html", fragment: "readable" }`: the
621
692
  the path and the rest is the channel (a spelling that opens with `#` names no path and stays
622
693
  whole), exactly as `worker:///a.html#readable` decomposes, so
623
694
  the `#channel` a READ receipt advertises (`channels: {"#readable": N}`) is addressable in the
624
- same bare spelling the receipt used (2026-09-13 dumbox demo: the model appended it and was
625
- told no entry existed at `users.html#readable`). `raw` is therefore always the path alone; a
695
+ same bare spelling the receipt used: a model that appends the channel to the path it was just
696
+ shown addresses the same entry. `raw` is therefore always the path alone; a
626
697
  bare path never contains a literal `#`, and `PlurnkParser.stringify` renders the channel back.
627
698
  Without a `#` the field is absent, so an older `LocalPath` literal stays valid.
628
699
 
@@ -642,9 +713,12 @@ Mutation semantics:
642
713
  - `<0>` prepends and `<-1>` appends.
643
714
  - §empty-mutation-scope Empty mutation content has one writable position: `<0>`, `<1>`, `<-1>`, and `<1,-1>` all insert the body as its complete value. Other scopes resolve against that same empty value through the ordinary coordinate algebra.
644
715
  - `<SL,SC,EL,EC>` deletes the exact exclusive-end region and inserts the body at its start.
645
- - §transfer-resource-selections COPY and MOVE require two singular `ResourceSelection` operands on the heading, source first and destination second, and admit no body. Each operand consists of `(path)`, optional `<scope>`, and optional `{metadata}` blocks; modifiers bind only to that operand. The two operands independently select their resource, channel, scheme metadata, and text region.
716
+ - §transfer-resource-selections COPY and MOVE require two singular
717
+ `ResourceSelection` operands, source first and destination second, and admit
718
+ no body. Each selection binds its own target, scope, matcher, and metadata
719
+ under {§slot-order}, {§matcher-option}, and {§scheme-metadata-modifier}.
646
720
 
647
- ### §operation-observation Per-operation observations
721
+ ### Per-operation observations
648
722
 
649
723
  | OP | Successful observation |
650
724
  |------|-----------------------------------------------------------------------------------|
@@ -654,16 +728,16 @@ Mutation semantics:
654
728
  | COPY | Source and destination selections plus ordered destination effects |
655
729
  | MOVE | Source and destination selections plus ordered destination and source effects |
656
730
  | SEND | Status and recipient acknowledgement when applicable |
657
- | EXEC | Spawn acknowledgement; output arrives through named stream channels |
731
+ | execution | Spawn acknowledgement; output arrives through named stream channels |
658
732
  | BARE | The one-shot model response |
659
733
  | WORK | Spawn acknowledgement; the deliverable arrives through the log |
660
734
  | FORK | Spawn acknowledgement; the inherited worker's deliverable arrives through the log |
661
735
  | KILL | Status of deletion or termination |
662
- | TASK | Current inventory and adjudicated lifecycle outcome |
736
+ | NOTE / WAIT | Literal memory or wait explanation |
663
737
 
664
738
  §find-result-unit For FIND, authored target shape fixes the paginated result
665
739
  unit. An exact target with a matcher pages flat match locations; a glob or
666
- folder target, and every body-less FIND, pages resources. Resolving a glob to
740
+ folder target, and every matcher-less FIND, pages resources. Resolving a glob to
667
741
  one resource does not make it exact. The same `<N>`, inclusive `<N,M>`,
668
742
  markerless `<1,16>`, and explicit-all `<1,-1>` forms apply to either unit.
669
743
 
@@ -676,7 +750,7 @@ EDIT. Whole-channel transfers remain bodyless structural effects; runtime
676
750
  owners reject binary markers rather than treating a text field as a byte lane.
677
751
 
678
752
  Every operation returns the runtime-neutral `OperationResult` defined by
679
- {§operation-result}. Its `status` belongs to the result envelope; TASK supplies
753
+ {§operation-result}. Its `status` belongs to the result envelope; a lifecycle operation supplies
680
754
  the authored lifecycle intent. Durable operation observations are projected into a later packet;
681
755
  retrieval never returns inline within the emitting turn.
682
756
 
@@ -733,12 +807,12 @@ never target content. Glob metacharacters remain legal path data.
733
807
  Matching and folder-scope semantics remain runtime concerns.
734
808
 
735
809
  §worker-name The exported `WORKER_NAME` contract governs names minted for URI
736
- authority slots: a lowercase DNS label matching
737
- `[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?`. `RESERVED_AUTHORITIES` contains the
738
- authority-shaped internal worker names `commons` and `plurnk`, which are
739
- unavailable for minting. `~` is the sole current-worker sigil and falls outside
740
- the mintable alphabet; every matching unreserved value, including `self`, is an
741
- ordinary literal worker name. This is a minting and registry invariant, not an
810
+ authority slots: `[A-Za-z0-9][A-Za-z0-9_-]{0,62}` (1–63 ASCII characters,
811
+ starting with a letter or digit). Case is preserved and significant;
812
+ `Approach_A` and `approach_a` are distinct names. There is no reserved-name list: the
813
+ runtime's own actor is named `_plurnk`, a spelling the predicate never admits.
814
+ Every matching value, including `self` and `plurnk`, is an ordinary
815
+ literal worker name. This is a minting and registry invariant, not an
742
816
  ingestion restriction: the parser decomposes arbitrary URL authorities.
743
817
 
744
818
  ## §matcher-prefix-claims 6. Bulk pattern matching
@@ -754,17 +828,17 @@ matching.
754
828
  statement-level error the parser discards the rest of that statement and resumes at the
755
829
  next heading; the turn shape is decided locally (a turn disposition is recognized by its own
756
830
  token, never by a whole-turn alternative), so one malformed heading costs one
757
- diagnostic and every later statement, the turn disposition included, stands on its own. Any
758
- other second path slot names the one-slot rule.
831
+ diagnostic and every later statement, the turn disposition included, stands on its own.
759
832
  - §scope-slot-tolerance A line scope written inside a path slot (```` ```COPY (worker:///src.md<2,3>) ````)
760
833
  is read as `(worker:///src.md) <2,3>` — `<` and `>` are not URI characters, so a `<…>` right
761
834
  before a slot's closing paren can only be a scope; every path slot of a statement is repaired
762
835
  the same way — and the slip is one warning-severity advisory at the `<`, placed right after its
763
836
  statement, stating the `(path) <scope>` form that was used. The statement runs; a warning is
764
837
  never a strike. A `<` anywhere else in the slot remains the lexer's refusal.
765
- - §second-path-slot A second `(path)` on a heading that already closed one is a parser
766
- error at the second paren stating the one-slot rule and that a pattern belongs in the
767
- `[{"pattern": …}]` option; the statement is dropped and its siblings run.
838
+ - §extra-path-slot A path slot beyond the operation's admitted operands is a parser
839
+ error at its opening paren. Report the unexpected slot and the grammar's expected
840
+ alternatives when available, without inferring pattern intent or imposing another
841
+ operation's operand count. The statement is dropped and its siblings run.
768
842
 
769
843
  | Prefix | Dialect | Canonical form | Typed admission | Runtime owner |
770
844
  |-----------|----------|--------------------------------------|-----------------------------------|---------------------|
@@ -816,9 +890,9 @@ The operation column names the canonical AST operation after
816
890
  | COPY/MOVE source | 0/1/2/4 text coordinates | Region copied or moved from the selected source |
817
891
  | COPY/MOVE destination | 0/1/2/4 text coordinates after target | Region replaced or insertion point at the destination |
818
892
  | KILL | 0/1/2 text coordinates | Whole target when absent; one physical line or inclusive range when present ({§kill-scope}) |
819
- | EXEC | `timeout[,poll]` | Spawn lifetime bound and poll cadence in minutes |
820
- | ```` ```TASK ```` | `timeout[,poll]` | Waiting intent: bounded or indefinite wait and optional poll cadence ({§send-wait-scope}) |
821
- | Directed SEND | Owner-defined numeric scope | Worker actors schedule a task with `delay[,interval]` ({§send-directed-scope}) |
893
+ | execution | `timeout[,poll]` | Spawn lifetime bound and poll cadence in minutes |
894
+ | WAIT | None | Scope is ignored ({§send-wait-scope}) |
895
+ | Directed SEND | Owner-defined numeric scope | Carried to the addressed owner; worker actors refuse it ({§send-directed-scope}) |
822
896
 
823
897
  Text coordinates use the algebra in {§text-scope-semantics}: one integer is a
824
898
  whole line, two integers are an inclusive whole-line range, and four integers
@@ -834,8 +908,7 @@ its `L`, `SL`, or `EL` position denotes a line. Columns, prepend `0`, and append
834
908
  `-1` remain numeric. Exact READ, EDIT, COPY/MOVE source and destination,
835
909
  KILL, and client LOOK preserve these positions in `TextLineMarker`; core resolves them
836
910
  against the addressed current text before operation-specific numeric scope
837
- semantics run. A matcher-bearing or path-glob READ normalizes to FIND, whose
838
- result positions remain numeric and reject anchors. Numeric text scopes remain
911
+ semantics run. FIND's result positions remain numeric and reject anchors. Numeric text scopes remain
839
912
  canonical and fully supported. Parser acceptance does not imply model-facing
840
913
  recommendation.
841
914
 
@@ -870,56 +943,49 @@ npm test
870
943
  ````
871
944
  `````
872
945
 
873
- The inner shell example is EDIT content, not an EXEC invocation. The same
946
+ The inner shell example is EDIT content, not an execution. The same
874
947
  rule protects code examples in SEND, WORK, FORK, BARE and every other body.
875
948
 
876
949
  ## 9. Turn dispositions
877
950
 
878
- TASK inventory intent maps to the existing HTTP-shaped lifecycle statuses
879
- ({§task-inventory-intent}). The runtime adjudicates that intent against actual
880
- results, obligations and timing:
951
+ The runtime adjudicates a nonempty admitted program against actual messages,
952
+ results and live obligations ({§wait-obligation-matrix}). A response with no
953
+ operation receives empty-turn recovery, not successful completion ({§empty-turn}).
881
954
 
882
955
  | Intent | Nominal status | Meaning |
883
956
  |---|---|---|
884
- | TASK omitted | 102 | Continue silently, without a receipt or strike for omission |
885
- | empty, continue, pending | 102 | Continue or recover; an explicit empty inventory is refused with a soft 409 receipt, no strike |
886
- | wait | 202 | Park when a live obligation or explicit timing exists |
887
- | complete | 200 | Conclude once execution results permit completion |
888
- | fail | 499 | End unsuccessfully and cancel unresolved descendant scope |
957
+ | Unanswered messages or unobserved results | 102 | Continue silently |
958
+ | WAIT | 202 | Park when a live obligation exists; otherwise continue at 102 |
959
+ | All messages answered, live work remains | 202 | Join the held work |
960
+ | All messages answered, results observed, no held work | 200 | The admitted program concludes; no repeated response is required |
961
+ | KILL own worker | 499 | Cancel unfinished work in that worker and its descendants |
889
962
  | Runtime or infrastructure failure | 5xx | Not a model-authored task status |
890
963
 
891
- ### §waitpid-dispositions The terminal contract (waitpid)
964
+ ### The terminal contract (waitpid)
892
965
 
893
- The model may supply one current inventory per turn; its statuses determine
894
- one intention. Without TASK, an operation-bearing turn continues silently.
895
- The engine verifies an explicit intention against the loop's actual
896
- obligations (spawned children, open streams, pending results); the grammar
897
- polices *shape* only. Asking
898
- the human is the native `question` EXEC tool ({§question-tool}), not a
966
+ The model may supply one WAIT per turn. The host, not the grammar, owns
967
+ turn boundaries and adjudicates the loop's actual obligations. Asking
968
+ the human is the native `question` executor tool ({§question-tool}), not a
899
969
  disposition. The shape rules ARE structural:
900
970
 
901
- - §send-mid-reservation TASK has a reserved token ({§turn-disposition}).
902
- A turn admits at most one TASK, anywhere among its operations
903
- ({§disposition-anywhere}); the runtime executes it last. A second
904
- disposition is a structural error, not a choice between competing outcomes.
905
- - §disposition-anywhere The disposition may sit anywhere in a model turn
906
- (operator, 2026-09-12: models state the plan first; the inventory is a
907
- statement about state, not a boundary). `PlurnkParser.parse` admits every
971
+ - §send-mid-reservation WAIT is reserved ({§turn-disposition}).
972
+ A turn admits any number of lifecycle declarations, anywhere among its
973
+ operations ({§disposition-anywhere}); the runtime executes them last, as one park.
974
+ - §disposition-anywhere A disposition may sit anywhere in a model turn
975
+ without imposing a program boundary. `PlurnkParser.parse` admits every
908
976
  operation before and after it in authored order; the runtime defers only the
909
- disposition until the other admitted operations settle
977
+ dispositions until the other admitted operations settle
910
978
  ({§op-execution-order}). Nothing is dropped and no diagnostic is raised for
911
- position. TASK omission does not synthesize a disposition ({§turn-shape}).
979
+ position. Omission does not synthesize a disposition ({§turn-shape}).
912
980
  - SEND is communication: an optional recipient path and an optional body.
913
- - §park-202-only TASK wait intent applies `<T>` (wait up to T minutes),
914
- `<T,P>` (adds a poll cadence, mirroring EXEC's slot), `<-1>`
915
- (indefinite; the join's own liveness bounds it). See §7 for the scope
916
- slot's shape. Other intents leave timing unapplied
917
- with a factual warning; timing does not override the inventory's intent.
918
- - §inventory-only-turn A TASK-only turn is valid for every inventory intent.
919
- Actionable work does not require an invented OP and does not imply parking.
981
+ - §park-202-only WAIT joins live work: an open stream or a live
982
+ child. With none, it continues. It takes no scope ({§send-wait-scope});
983
+ a future message is scheduled through the schedule family.
984
+ - §lifecycle-only-turn A WAIT-, SEND-, or NOTE-only turn is valid.
985
+ NOTE does not request parking or acknowledge messages.
920
986
  Ordinary repetition, strike and execution limits still apply.
921
987
 
922
- SEND with no `(path)` responds to the Active Prompts without ending the turn. SEND with
988
+ SEND with no `(path)` answers the open messages without ending the turn. SEND with
923
989
  `(path)` directs the message to that recipient. Neither changes loop status.
924
990
 
925
991
  ### §send-body SEND body projection
@@ -932,17 +998,22 @@ defines no synthetic scheme or READ-back convention for them.
932
998
 
933
999
  ## §parser-architecture 10. Parser architecture
934
1000
 
1001
+ The implementation this section describes lives in `@plurnk/plurnk-parser`
1002
+ ({§parser-consumers}); this section remains the contract it implements.
1003
+
935
1004
  ANTLR owns framing, slots and statement composition; AstBuilder produces the
936
1005
  schema-owned AST. Registration, effects and authority remain runtime concerns.
937
1006
 
938
1007
  ```mermaid
939
1008
  stateDiagram-v2
940
1009
  [*] --> DEFAULT
1010
+ DEFAULT --> QUOTATION: a fence that opens no operation
1011
+ QUOTATION --> DEFAULT: its matching closer
941
1012
  DEFAULT --> SLOTS: fenced native OP or executor
942
1013
  SLOTS --> TARGET: (
943
1014
  TARGET --> SLOTS: )
944
- SLOTS --> METADATA: {
945
- METADATA --> SLOTS: }
1015
+ SLOTS --> METADATA: [
1016
+ METADATA --> SLOTS: ]
946
1017
  SLOTS --> BODY: header newline or tolerated inline body
947
1018
  SLOTS --> DEFAULT: matching compact closer
948
1019
  BODY --> DEFAULT: matching standalone closer, no nested block
@@ -960,13 +1031,13 @@ already ends in one. Interstatement whitespace belongs to no body.
960
1031
  A header starts at column zero; the first operation may follow provider preamble
961
1032
  without a separating newline. Text outside operation blocks is ignored in every
962
1033
  parser tier: before, between, and after operations. It produces no AST item,
963
- message, receipt, or diagnostic. Exact source remains in `ops:///` under
1034
+ message, receipt, or diagnostic. Exact source remains in `ops://<worker>/` under
964
1035
  {§turn-ops-log-curation}; body bytes and source positions are unchanged. A matching
965
1036
  closer still ends its body, and no missing closer is inferred. No generic Markdown
966
1037
  rendering, indentation stripping or recursive code-block extraction occurs.
967
1038
  Only a header aside has aside semantics.
968
1039
 
969
- ## §public-api 12. Public API
1040
+ ## 12. Public API
970
1041
 
971
1042
  The package root is the single JavaScript and TypeScript entry point. Shared AST
972
1043
  and wire types come from generated schemas; the small hand-maintained parser
@@ -977,26 +1048,23 @@ express. Consumers never receive ANTLR parse-tree or token types.
977
1048
  operation is reported by one hard diagnostic (`no valid Plurnk operation was
978
1049
  found.`), which the host may admit as an empty turn rather than reject
979
1050
  (plurnk-core `§empty-turn`). An explicit disposition may sit anywhere in it
980
- ({§disposition-anywhere}). Omitted TASK
981
- means silent continuation: no synthesized statement, diagnostic, receipt,
1051
+ ({§disposition-anywhere}). An omitted WAIT produces no synthesized statement, diagnostic, receipt,
982
1052
  warning, or strike. The authored operations and source remain unchanged.
983
- Explicit empty or malformed inventories retain their own handling.
984
1053
  Unfinished blocks never receive inferred closers.
985
- Bounded operation errors retain valid siblings. Duplicate dispositions
986
- and failed document boundaries remain structural failures.
1054
+ Bounded operation errors retain valid siblings, and so does a lost boundary:
1055
+ the statements that closed before `unparsedTail.from` are facts, and only what
1056
+ follows is undefined ({§unparsed-tail-boundary}).
987
1057
 
988
- `parseLog` reads consecutive saved turns separated by their dispositions and
989
- requires their dispositions; a saved turn is stored per turn, so a mid-turn
990
- disposition never needs splitting. There is no outer Markdown program wrapper;
991
- the executable blocks themselves are the program.
1058
+ The host records programs per turn; no operation acts as a separator between
1059
+ saved programs. There is no outer Markdown program wrapper; the executable
1060
+ blocks themselves are the program.
992
1061
 
993
1062
  §tier-entrypoints Each parser entry point owns one document tier:
994
1063
 
995
1064
  | Entry point | Accepted document | Result statement type |
996
1065
  |--------------------------------|----------------------------------------------------------------|-----------------------|
997
- | `PlurnkParser.parse` | One operation-bearing model turn; at most one TASK, anywhere | `PlurnkStatement` |
1066
+ | `PlurnkParser.parse` | One operation-bearing model turn; at most one lifecycle declaration, anywhere | `PlurnkStatement` |
998
1067
  | `PlurnkParser.parseStatements` | Zero or more protocol statements | `PlurnkStatement` |
999
- | `PlurnkParser.parseLog` | One or more consecutive disposition-ended turns | `PlurnkStatement` |
1000
1068
  | `PlurnkParser.parseClient` | Executable blocks, including the read-shaped LOOK command | `ClientStatement` |
1001
1069
 
1002
1070
  Every entry point ignores outside text under {§whitespace-contract} and returns
@@ -1004,26 +1072,28 @@ ordered `statement` and `error` items. When present, {§unparsed-tail-boundary}
1004
1072
  extent. The statement `op` field discriminates the generated per-operation
1005
1073
  union.
1006
1074
 
1007
- §root-value-api The package-root runtime namespace is closed and consists of the
1008
- following supported consumer values. All other root exports are TypeScript types.
1009
-
1010
- | Root value(s) | Consumer contract | Exact owner |
1011
- |---------------------------------------|---------------------------------------------------------------------|---------------------------------------------|
1012
- | `PlurnkParser` | Four document-tier entry points listed above | {§parser-architecture}, {§tier-entrypoints} |
1013
- | `PlurnkParseError` | JSON-serializable positioned parser diagnostic | {§parse-diagnostics} |
1014
- | `parsePath` | Parser-equivalent target admission | {§path-syntax}, {§tier-entrypoints} |
1015
- | `PathSyntax` | Target-slot spelling and exact-versus-glob classification | {§path-parentheses}, {§path-glob} |
1016
- | `Validator` | Validation and assertion against the owning JSON Schemas | {§wire-entrypoint} |
1017
- | `InvalidNoticeError` | Typed failure from `Validator.assertNotice` | {§notice} |
1018
- | `InvalidProblemDetailsError` | Typed failure from `Validator.assertProblemDetails` | {§problem-details} |
1019
- | `InvalidProblemProjectionError` | Typed failure from `Validator.assertProblemProjection` | {§problem-projection} |
1020
- | `InvalidOperationResultError` | Typed failure from `Validator.assertOperationResult` | {§operation-result} |
1021
- | `InvalidTextRegionError` | Typed failure from `Validator.assertTextRegion` | {§text-region} |
1022
- | `InvalidRangeExtentError` | Typed failure from `Validator.assertRangeExtent` | {§range-extent} |
1023
- | `Problems` | RFC 9457 Problem construction and model projection | {§problem-details}, {§problem-projection} |
1024
- | `PLURNK_OPS` | Runtime tuple from which the closed `PlurnkOp` union is derived | {§canonical-statement} |
1025
- | `WORKER_NAME`, `RESERVED_AUTHORITIES` | Authority minting predicate and internal reserved names | {§worker-name} |
1026
- | `UNKNOWN_POSITION` | Frozen sentinel for an AST statement without retained parsed source | {§parser-position} |
1075
+ §root-value-api The package-root runtime namespace is closed. Its exact membership has one home,
1076
+ `src/index.test.ts`, which fails on any addition or removal; the table names the families and
1077
+ their owners. All other root exports are TypeScript types. `PlurnkParser` and `parsePath` are not
1078
+ among them: the parser is `@plurnk/plurnk-parser`'s ({§parser-consumers}).
1079
+
1080
+ | Root value(s) | Consumer contract | Exact owner |
1081
+ |-----------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------|
1082
+ | `Validator` and one `Invalid…Error` per assertion | Validation and typed failure against the owning JSON Schemas | {§wire-entrypoint} |
1083
+ | `Problems` | RFC 9457 Problem construction and model projection | {§problem-details}, {§problem-projection} |
1084
+ | `PlurnkParseError` | JSON-serializable positioned parser diagnostic | {§parse-diagnostics} |
1085
+ | `PathSyntax` | Target-slot spelling and exact-versus-glob classification | {§path-parentheses}, {§path-glob} |
1086
+ | `TurnDisposition` | The one turn disposition and its recognition | {§turn-disposition} |
1087
+ | `CapabilityAdmission` | Admission of a capability descriptor against policy layers | {§capability-admission} |
1088
+ | `PLURNK_OPS`, `INTERNAL_ROW_OPS`, `PLURNK_FENCE` | The closed operation alphabet and the language's fence | {§canonical-statement} |
1089
+ | `PROPOSAL_POLICIES` | The vocabulary a loop policy chooses from | {§loop-policy} |
1090
+ | `WORKER_NAME` | Authority minting predicate | {§worker-name} |
1091
+ | `UNKNOWN_POSITION` | Frozen sentinel for an AST statement without retained parsed source | {§parser-position} |
1092
+
1093
+ The remaining values are small pure helpers over those contracts (`isExecution`, `writtenOp`,
1094
+ `lifecycleOfLoopStatus`, `selectWorkerLoop`, `renderJsonResult`, `formatJsonDocument`,
1095
+ `aguiConformanceReport`) and the closed name patterns and vocabularies (`RUNTIME_TAG`,
1096
+ `SKILL_NAME`, `REASONING_POLICIES`).
1027
1097
 
1028
1098
  §parser-construction-boundary Parser construction components are internal rather
1029
1099
  than alternate consumer entry points:
@@ -1033,20 +1103,10 @@ than alternate consumer entry points:
1033
1103
  | `AstBuilder` | Consumes generated ANTLR contexts; `PlurnkParser` and `parsePath` own its API |
1034
1104
  | `PlurnkErrorStrategy`, `RecordingListener` | Assemble parser recovery and diagnostics around `antlr4ng`; consumers receive `PlurnkParseError` values |
1035
1105
 
1036
- ### CLI
1037
-
1038
- ```text
1039
- plurnk-contracts [file] parse a file, or standard input when omitted
1040
- plurnk-contracts --help show usage
1041
- ```
1042
-
1043
- The CLI prints the parse result as JSON. It exits `0` when no error item or
1044
- `unparsedTail` exists and `1` otherwise.
1045
-
1046
1106
  ## 13. Runtime-neutral wire contracts
1047
1107
 
1048
1108
  §wire-entrypoint The package root exports generated wire types, `Problems`, and
1049
- `Validator` alongside the parser and AST. Their owning JSON Schemas are published
1109
+ `Validator` alongside the AST types. Their owning JSON Schemas are published
1050
1110
  through `@plurnk/plurnk-contracts/schema/*.json`, not re-exported as root values.
1051
1111
 
1052
1112
  ### §text-region 13.1 Text regions
@@ -1164,6 +1224,19 @@ Internal invariant violations throw and preserve their cause. An external
1164
1224
  protocol may require its own error envelope; its adapter maps that envelope to
1165
1225
  or from the canonical Problem without creating another PLURNK failure contract.
1166
1226
 
1227
+ §problem-error-carrier `Problems.fromError(error)` recognizes existing failure
1228
+ carriers without depending on a producer's exception class:
1229
+
1230
+ | Carrier | Interpretation |
1231
+ |---------|----------------|
1232
+ | `error.result` present | Validate the complete {§operation-result}; return its Problem, if any |
1233
+ | Otherwise, `error.problem` present | Validate and return {§problem-details} unchanged |
1234
+ | Absent or malformed carrier | Return `null`; the caller retains the original exception as an unexpected failure |
1235
+
1236
+ Recognition never derives recovery text from an exception message, changes its
1237
+ status, or rescues a malformed result through a second Problem field. Unexpected
1238
+ accessor or validator exceptions propagate.
1239
+
1167
1240
  §problem-projection `ProblemProjection` is the sole compact model-packet view of
1168
1241
  an exact `ProblemDetails`. `Problems.project(problem, context)` validates both
1169
1242
  representations and rejects a status that contradicts the enclosing row.
@@ -1225,22 +1298,23 @@ client ID plus symbolic secret, or neither for server-advertised Dynamic Client
1225
1298
  Registration fallback. A definition cannot combine those identity modes.
1226
1299
 
1227
1300
  §mcp-configuration-overlay `McpConfigurationOverlay` is the bounded raw
1228
- configuration projection a client may carry to MCP list and enable actions. It
1229
- contains only string-valued `PLURNK_MCP_*` server declaration variables;
1230
- service-owned connection/request timeouts and default enabledness are excluded.
1301
+ configuration projection a client may carry to MCP list and enable actions: its
1302
+ string-valued `PLURNK_MCP_*` variables, whole. Which of those names are the
1303
+ host's own controls is the host's fact alone — its parser skips every control
1304
+ it owns, so a carried timeout or enabled list has no effect and no client or
1305
+ contract restates that vocabulary.
1231
1306
  The client does not interpret this map. The MCP host composes it over the
1232
1307
  lower normalized definition through the same parser that admits service
1233
1308
  environment declarations, then validates the resulting
1234
1309
  `McpServerDefinition`. Carrying the overlay does not connect, persist, or
1235
1310
  expand credentials by itself.
1236
1311
 
1237
- `SkillDefinition` is the one definition the Worker `skills` Functionality
1238
- family accepts and persists: the standard Agent Skills `name` (the directory
1239
- name), the universal root `scope` (`project` or `global`), and — for a
1240
- Worker-installed skill — the standard installer package `source` that
1241
- provides it. `Validator.assertSkillDefinition` is the family's admission
1242
- boundary; the filesystem under the scope's root, never the definition, is the
1243
- truth about installation.
1312
+ `SkillDefinition` is the one definition the workspace `skills` Functionality
1313
+ family accepts and persists: the standard `name`, source `scope`, and optional
1314
+ installer `source`. `Validator.assertSkillDefinition` validates the wire shape;
1315
+ the schema's name grammar is exposed as `SKILL_NAME` for loaders and discovery
1316
+ ({§agent-skills-name}). Core owns installation truth and lifecycle
1317
+ ({§skills-functionality}).
1244
1318
 
1245
1319
  `A2aAgentDefinition` is the one definition the Worker `a2a` Functionality
1246
1320
  family accepts and persists: the local alias `name` (the `a2a://<name>`
@@ -1264,11 +1338,22 @@ interaction, and event owners through this port.
1264
1338
  from user-authored prompt content. An adapter may expose no public means to set
1265
1339
  it; Core validates and records it through the same prompt admission path.
1266
1340
 
1341
+ `runLoop.attachments` carries typed bytes selected by the adapter;
1342
+ `runLoop.envelope` retains opaque protocol evidence under
1343
+ {§message-envelope-evidence}. `readMessages` projects durable inbox messages
1344
+ and successful conversation replies independently of log visibility, retaining
1345
+ each reply's `answers` addresses (empty for incoming messages).
1346
+ `resolveClientInteraction` may carry the accepted answer's message evidence;
1347
+ Core validates the resolution, retains the arrival, then resumes the operation.
1348
+
1267
1349
  §application-worker-observation Worker observation exposes durable identity,
1268
1350
  origin, immediate parent identity, minted `kind` (`conversation`; `fork` for a
1269
1351
  child carrying a fork boundary; `work` for any other child), and `lifecycle`,
1270
- the worker's latest work loop projected through {§loop-lifecycle-vocabulary} (`idle`
1271
- when it has none). Maintenance-only loops do not change this projection;
1352
+ the worker's representative work loop projected through {§loop-lifecycle-vocabulary} (`idle`
1353
+ when it has none). `selectWorkerLoop` chooses running before parked before queued;
1354
+ ties select the oldest unresolved sequence. With no live work, the latest terminal
1355
+ settlement wins (sequence breaks equal timestamps). Newer terminal history never
1356
+ hides unfinished work. Maintenance-only loops do not change this projection;
1272
1357
  their ordinary turns and results remain durable history. `readWorker` resolves exactly one id or name and returns
1273
1358
  `null` when absent. `listWorkers` filters collections by origin or lineage
1274
1359
  position; an omitted parent filter means every position and an explicit `null`
@@ -1286,9 +1371,7 @@ in `@plurnk/plurnk-contracts` is that projection's one owner.
1286
1371
  §application-loop-observation Loop observation exposes the durable scheduler
1287
1372
  state of work loops (excluding maintenance-only administrative loops), exact terminal `OperationResult`, and exact count of packet-bearing
1288
1373
  Turns for one owned Worker. Packetless producer Turns and physical provider
1289
- retries do not contribute to `packetCount`. Scheduled tasks expose `scheduledAt`
1290
- (ISO date), optional `intervalMinutes`, and `recurrenceId` (the original task id).
1291
- Packet notifications carry the same timing; ordinary tasks omit it. Exterior
1374
+ retries do not contribute to `packetCount`. Exterior
1292
1375
  adapters consume this projection instead of reconstructing lifecycle from
1293
1376
  events or persistence; events remain the live notification edge.
1294
1377
 
@@ -1307,7 +1390,6 @@ class PlurnkParseError extends Error {
1307
1390
  readonly column: number;
1308
1391
  readonly source: ErrorSource;
1309
1392
  readonly severity: Severity;
1310
- readonly code?: "invalid-turn-structure";
1311
1393
  }
1312
1394
  ```
1313
1395
 
@@ -1345,10 +1427,8 @@ and 3.30.2](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html).
1345
1427
  the sole and complete owner of syntax-error messaging because it holds the
1346
1428
  parse state, lexer mode, and expected-token set that no consumer has. It
1347
1429
  produces the final diagnostic message, deduplicated expected-token lists, and
1348
- turn-shape diagnostics ({§turn-shape}). Omitted TASK produces no diagnostic, and
1349
- neither does the position of a present one ({§disposition-anywhere}). A failed
1350
- document boundary carries `code: "invalid-turn-structure"`, which cannot be
1351
- recovered as an individual failed operation. Source with no
1430
+ turn-shape diagnostics ({§turn-shape}). An omitted lifecycle declaration produces no diagnostic, and
1431
+ neither does the position of a present one ({§disposition-anywhere}). Source with no
1352
1432
  parsed operation yields `no valid Plurnk operation was found.` Targeted
1353
1433
  diagnostics are:
1354
1434
 
@@ -1358,9 +1438,7 @@ diagnostics are:
1358
1438
  its flags (`/(?i)shutdown|reactor/` is `/shutdown|reactor/i`; `^(?i)note:` is the
1359
1439
  anchored regex with `i`), with one warning-severity advisory naming the flag
1360
1440
  position after the closing `/`. Only a leading group of `i`, `m` and `s` lifts;
1361
- `(?i:…)` scoped modifiers are valid ECMAScript and pass through untouched. From
1362
- the 2026-09-13 dumbox demo pass, where the model wrote the inline form, was
1363
- refused, and rewrote it as a trailing flag one turn later.
1441
+ `(?i:…)` scoped modifiers are valid ECMAScript and pass through untouched.
1364
1442
  - §regex-trailing-text A valid `/pattern/flags` prefix followed by horizontal
1365
1443
  whitespace and trailing text receives one concise trailing-content
1366
1444
  diagnostic, with or without flags, without assuming what the extra text was
@@ -1371,7 +1449,9 @@ diagnostics are:
1371
1449
  matcher, in whichever dialect its first characters claim
1372
1450
  ({§matcher-prefix-claims}): `/re/i`, `^anchored`, `//xpath`, `$.json`, `~words`,
1373
1451
  `&symbol`, or a sigil-less glob or literal such as `TODO` or `*.ts`. Those
1374
- operations take no body, so heading-line text can mean nothing else. On EDIT only
1452
+ matchers remain independent of owner options: a block without `pattern` never
1453
+ erases the heading matcher, and invalid blocks still reach the owning validator.
1454
+ These operations take no body, so heading-line text can mean nothing else. On EDIT only
1375
1455
  a sigil lifts, because plain heading-line text is the replacement body it always
1376
1456
  was; the lines beneath the heading are then the replacement, and none deletes each
1377
1457
  match ({§edit-pattern}). A trailing `<!-- aside -->` on the same line stays the
@@ -1381,8 +1461,7 @@ diagnostics are:
1381
1461
  `FIND (src/parser.ts) /\bparse\w+\b/` is the same operation as
1382
1462
  `FIND (src/parser.ts) [{"pattern": "/\\bparse\\w+\\b/"}]`. `^` claims the regex
1383
1463
  dialect without slashes or flags: the whole text is the pattern, so
1384
- `READ (reasoning:///1/1) ^NOTE:.*` selects a turn's note lines (operator,
1385
- 2026-09-12: "Recursive Reasoning").
1464
+ `READ (notes.md) ^Decision:.*` selects lines beginning with `Decision:`.
1386
1465
  - §trailing-slots **Slots after the matcher peel off the right.** The heading text after
1387
1466
  the matcher is read backwards: a trailing `<!-- aside -->`, a trailing `<scope>` in the
1388
1467
  shapes the lexer admits (result positions on FIND, text coordinates elsewhere) and a
@@ -1390,9 +1469,8 @@ diagnostics are:
1390
1469
  any order, each taken once and only when the heading did not already carry that slot,
1391
1470
  until what remains is the matcher. `READ (a.rs) /fn resolve_/ <1,-1> <!-- entities -->`
1392
1471
  is the same operation as `READ (a.rs) <1,-1> /fn resolve_/ <!-- entities -->`, with
1393
- one warning-severity advisory naming the canonical order for the scope or block
1394
- (operator, 2026-09-13: "swallow up anything that passes as legitimate plurnk"; the
1395
- 2026-09-13 dumbox run refused three headings for this in one turn). A matcher that
1472
+ one warning-severity advisory naming the canonical order for the scope or block:
1473
+ the grammar swallows up anything that passes as legitimate plurnk. A matcher that
1396
1474
  itself ends in one of those shapes takes the option escape.
1397
1475
  - §matcher-body-redirect **A body beneath those headings.** Text below the heading
1398
1476
  of a FIND, READ or KILL is a body, and those operations take none: the builder
@@ -1413,7 +1491,7 @@ diagnostics are:
1413
1491
  - §invalid-scope-diagnostic **Malformed scope content.** After a properly spaced
1414
1492
  scope opener, report the offending scope (at most 64 code points, ending at
1415
1493
  `>` or the heading's line end) and its operation's constraint: FIND result
1416
- positions, EXEC/TASK minutes, text coordinates, or no scope. Do not append advice for
1494
+ positions, execution minutes, text coordinates, or no scope. Do not append advice for
1417
1495
  other operations or infer why the producer supplied the value. Spacing and
1418
1496
  boundary-loss diagnostics retain their own contracts.
1419
1497
  - §misplaced-aside-advisory **Aside in the body.** A READ or FIND whose
@@ -1427,7 +1505,10 @@ diagnostics are:
1427
1505
  ({§matcher-body-redirect}).
1428
1506
 
1429
1507
  §error-shape The diagnostic class determines how much guidance the parser may
1430
- provide:
1508
+ provide. Advisories belong to one successfully built statement. A rejected
1509
+ statement emits its hard diagnostic, not normalization advisories; neither a
1510
+ rejection nor an internal exception carries advisories into another statement
1511
+ or parser invocation.
1431
1512
 
1432
1513
  | Class | Surface | Message contract |
1433
1514
  |------------------------|-----------------------|-------------------------------------------------------------------------------------------|
@@ -1449,7 +1530,7 @@ Examples of canonical hard facts:
1449
1530
  - `unrecognized character '<' in target`
1450
1531
  - `unexpected bracket modifier; the fence name selects the executor`
1451
1532
  - `unrecognized character 'X' in statement header`
1452
- - `TASK's body begins below the header`
1533
+ - `WAIT's body begins below the header`
1453
1534
  - `expected ')'; got ':'`
1454
1535
 
1455
1536
  Each malformed statement produces at most one hard error. The first recorded
@@ -1466,7 +1547,9 @@ without a closer is not such a case: it ends under {§closer-fallback}. `ParseRe
1466
1547
  before that point; recovered contexts and diagnostics at or beyond it are not
1467
1548
  public results. The tail is one separate boundary fact, not an additional
1468
1549
  malformed-statement diagnostic. Consumers must treat anything from that point
1469
- onward as undefined and must never dispatch a recovered statement from it.
1550
+ onward as undefined and must never dispatch a recovered statement from it; the
1551
+ facts before it are ordinary facts, and a consumer that runs them owes the
1552
+ author the tail's reason.
1470
1553
 
1471
1554
  | Consumer duty | Contract |
1472
1555
  |--------------------|----------------------------------------------------------------------------------------------------------------|
@@ -1485,6 +1568,6 @@ runtime constructs this; the parser provides the fields):
1485
1568
  "column": 12,
1486
1569
  "source": "parser",
1487
1570
  "severity": "error",
1488
- "message": "READ block opened at line 1 but was not closed with 3 backticks"
1571
+ "message": "READ block opened at line 1 but was not closed with 4 backticks"
1489
1572
  }
1490
1573
  ```