@plurnk/plurnk-contracts 3.0.0 → 3.1.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.
package/README.md CHANGED
@@ -1,9 +1,8 @@
1
1
  # @plurnk/plurnk-contracts
2
2
 
3
3
  The single authority for PLURNK's model-facing language contract, its AST,
4
- generated model rail, shared schemas and types, runtime-neutral Problems,
5
- operation results, Notices, and text coordinates. See SPEC
6
- {§root-value-api}.
4
+ shared schemas and types, runtime-neutral Problems, operation results,
5
+ Notices, and text coordinates. See SPEC {§root-value-api}.
7
6
 
8
7
  ## Install
9
8
 
@@ -27,8 +26,7 @@ Requires Node.js 26 or newer.
27
26
  JSON Schema owns shared wire shapes, generated TypeScript projects those
28
27
  shapes, and ANTLR owns accepted model-language syntax. See SPEC
29
28
  {§contract-representations}. An operator who wants constrained sampling on a
30
- llama-server route writes their own GBNF and points `PLURNK_PROVIDERS_GBNF` at
31
- it; this package ships no grammar profile.
29
+ llama-server route writes their own GBNF and points `PLURNK_PROVIDERS_GBNF` at it.
32
30
 
33
31
  ## Parser
34
32
 
package/SPEC.md CHANGED
@@ -28,8 +28,8 @@ runtime-neutral wire envelopes; `@plurnk/plurnk-parser` implements the language
28
28
 
29
29
  §contract-representations JSON Schema is authoritative for shared data shapes. TypeScript types are
30
30
  generated from the schemas; ANTLR is authoritative for accepted model-language
31
- syntax. No generation grammar is generated or shipped; an operator's own GBNF
32
- is carried verbatim to a llama-server route by the providers package.
31
+ syntax. An operator's own GBNF is carried verbatim to a llama-server route by
32
+ the providers package.
33
33
 
34
34
  §agui-discovery-contract `AguiDiscovery` is the complete installed AG-UI+
35
35
  surface at one instant. `schemaVersion` identifies its discovery shape;
@@ -128,7 +128,7 @@ meaning, including registered scheme resolution, target existence, tag
128
128
  selection, text-region bounds, result ordering, full-text ranking, mutation
129
129
  effects, and executor behavior.
130
130
 
131
- ### §contract-proposal-projection Loop policy and stopped-world projection
131
+ ### §contract-proposal-projection Admission policy and stopped-world projection
132
132
 
133
133
  The schemas own the runtime-neutral shapes; core owns their stateful values.
134
134
 
@@ -137,7 +137,7 @@ The schemas own the runtime-neutral shapes; core owns their stateful values.
137
137
  | `CapabilityDescriptor` | One routed operation demand with its operation, access class, resource/runtime/tool coordinates, and declared traits | Derive every demand before dispatch |
138
138
  | `CapabilityPolicy` | Exact `only`/`deny` selectors; omitted `only` is unrestricted and present empty `only` denies all | Intersect service and workspace layers |
139
139
  | `CapabilityProjection` | Exact service, workspace, and normalized effective policies | Expose the resolver's workspace cascade without claiming one layer is effective authority |
140
- | `WorkerOwner` | Nonempty address and unique client-tool names | Persist by workspace; inherit through worker creation |
140
+ | `WorkerOwner` | Nonempty address, unique client-tool names, and whether it is interactive | Persist by workspace; inherit through worker creation |
141
141
  | `ProposalPolicy` | `review`, `accept`, or `reject` | Read the server's disposition knob at the proposal boundary |
142
142
  | `ProposalDisposition` | Review, or automatic accept/reject with optional outcome | Select once for the pending gate |
143
143
  | `ProposalProjection` | Identity, review target, body/attrs, owner, stale signal, disposition | One validated projection for live delivery and reconnect |
@@ -164,13 +164,16 @@ than hidden policy behavior.
164
164
 
165
165
  §capability-policy-projection A `CapabilityProjection` reports every durable
166
166
  workspace layer and their normalized intersection: `service`, `workspace`, and
167
- `effective`. Only `workspace` is client-mutable. Workers and loops have no
168
- capability policy or inherited bound; every actor uses the same live workspace
169
- policy. A client never derives effective authority from the mutable layer alone.
167
+ `effective`. Only `workspace` is client-mutable. Workers and loops store no
168
+ capability policy and inherit no bound; every actor uses the same live workspace
169
+ policy. Core derives one further ring per asking worker from its owner, and it only
170
+ removes interaction runtimes ({§owner-interaction-ring}). A client never derives
171
+ effective authority from the mutable layer alone.
170
172
 
171
173
  §proposal-policy `ProposalPolicy` names one server disposition; `PROPOSAL_POLICIES`
172
- is its schema-owned vocabulary. `WorkerOwner` carries an address and the client-tool
173
- names that recipient implements. Neither contains resource permissions or
174
+ is its schema-owned vocabulary. `WorkerOwner` carries an address, the client-tool
175
+ names that recipient implements, and `interactive`: whether a person attends it. Neither
176
+ contains resource permissions or
174
177
  message-carried authority. Core owns persistence, inheritance and settlement
175
178
  ({§worker-ownership}, {§proposal-disposition}); capability admission precedes approval.
176
179
 
@@ -311,15 +314,14 @@ column-zero line outside any block containing a native operation's name and at m
311
314
  `<!-- aside -->`, with only horizontal whitespace otherwise, opens that operation as if it were
312
315
  fenced with the taught three backticks. The aside retains its ordinary meaning: no target or
313
316
  modifiers are inferred. Its body runs to a line that is exactly the name
314
- again, to the next heading ({§fence-heading-in-body}), or to the end of the turn, except
315
- parameterless KILL always takes the remaining answer region ({§terminal-kill}). A fence inside
317
+ again, to the next heading ({§fence-heading-in-body}), or to the end of the turn. A fence inside
316
318
  naming nothing known is body; the block expects no closer, so its body is never cut
317
- back ({§closer-fallback}). A naked `KILL` alone holds every fence inside it as text ({§naked-kill}). It runs, and one warning-severity receipt follows its statement —
318
- `` `KILL` opened with no fence; the taught form is three backticks. `` One rule for every native
319
+ back ({§closer-fallback}). One warning-severity receipt follows a recovered statement —
320
+ `` `NOTE` opened with no fence; the taught form is three backticks. `` One rule for every native
319
321
  operation: a naked `WAIT` parks, a naked `NOTE` takes its text, a naked `READ` meets the ordinary
320
322
  missing-target refusal. A closer the author wrote anyway is still not body: when the body's last line is
321
323
  a bare backtick fence that no other fence line in the body pairs with (an odd count of fence lines), that
322
- line is the block's closer and leaves the body, so `KILL`, then the answer, then a closing fence delivers
324
+ line is the block's closer and leaves the body, so `SEND`, then the answer, then a closing fence delivers
323
325
  the answer alone. An aside-bearing name inside a body is not a closer. An incomplete or repeated
324
326
  aside, an operand, or other heading text does not qualify and remains the unfenced form
325
327
  ({§unfenced-operation}). Executors are runtimes rather than operations, and reasoning is never
@@ -329,10 +331,9 @@ read this way.
329
331
  line of three or more backticks and a name that is a native operation or a known executor
330
332
  is a heading. Inside an open block it either ends that block without closing it and opens the
331
333
  next statement, or it is a literal example held by the block. {§fence-pairing} chooses between
332
- the two; parameterless KILL always holds the remaining input ({§terminal-kill}). Fence lines of
334
+ the two. Fence lines of
333
335
  fewer than three backticks are never headings ({§operation-fences}). Known executors are `sh`
334
- plus what the host names in `ParseOptions.executors`. Inside a parameterless `KILL` a
335
- heading never ends the block ({§terminal-kill}).
336
+ plus what the host names in `ParseOptions.executors`.
336
337
 
337
338
  §closer-fallback A block that ends at a heading or at the end of the input has no closer of its
338
339
  own; its body is the whole span less one terminating line ending. Where a same-character fence
@@ -443,17 +444,9 @@ heading. An operation example quoted in a body of its own width therefore reads
443
444
  closing and the example running; the wider outer fence or the tab offset quotes it
444
445
  ({§operation-fences}).
445
446
 
446
- §terminal-kill **A parameterless KILL starts the final answer region.** Once its heading is
447
- recognized as an operation, the rest of the input is its literal body. A fence cannot return
448
- that region to executable operations or outside text. Apparent operations, receipts and malformed
449
- fences within it are answer text. An actual enclosing closer at the end is framing, not body;
450
- an earlier fence remains body. Compact and recovered naked spellings obey the same boundary.
451
- Quoted KILL examples do not open this region; targeted KILL retains ordinary statement framing.
452
- Earlier operations still execute and completion remains subject to {§kill-conclusion}.
453
-
454
447
  §prose-code-blocks **A message or a prompt shows code.** Inside a `SEND`, `WORK`, `FORK` or `BARE`
455
448
  body, at any depth, a heading that names an executor (` ```sh `, ` ```python3 `) is a code block the
456
- text shows, exactly as under {§terminal-kill}: it never ends the block and never runs. A native
449
+ text shows: it never ends the block and never runs. A native
457
450
  operation's heading still ends such a block, since a message is often followed by the author's next
458
451
  operation. In the distinct recorded benchmark emissions an executor heading ended an unclosed
459
452
  `WORK` body 14 times, a `BARE` body 9 times and a `SEND` body twice; every one inspected (qflash
@@ -470,10 +463,6 @@ operations the author wrote than the best repaired reading, the repaired reading
470
463
  inner fence keep what followed it (the qflash run192, run90 and run210 deliverables regain their last
471
464
  sections), one echoed transcript (glm run155) runs one more operation, and none loses one.
472
465
 
473
- §naked-kill **A naked KILL obeys the same answer boundary.** A parameterless `KILL` opened without
474
- a fence ({§naked-operation}), with or without an aside, takes the remaining input under
475
- {§terminal-kill}. A repeated name can close it only at the end; it never exposes later operations.
476
-
477
466
  §pairing-witness **Witnesses.**
478
467
 
479
468
  | Witness | Result |
@@ -558,9 +547,8 @@ opening fence, OP, parameters, and aside share one line. `` Saying that it ran k
558
547
  issuing it again. Inside a body the same lines stay literal. Measured: of 2,308 naked fences in
559
548
  12,797 recorded emissions, 51 open that way and 2,238 hold code or quoted output, which is why a
560
549
  bare fence alone draws nothing; in the 10,486-emission fence corpus every one of the 12 that open
561
- that way was an operation the model meant to run. With the answer carried by the final KILL,
562
- whose body shows and never runs ({§terminal-kill}), an example the model means only to show lives
563
- in a body.
550
+ that way was an operation the model meant to run. An example the model means only to show
551
+ belongs in a properly nested body ({§balanced-fences}) or a quotation ({§quotation}).
564
552
  So a model may show plurnk's own operations in an answer. Two exceptions keep programs whole:
565
553
  CommonMark's own rule that a backtick opener's line carries no further backtick, so
566
554
  ```` ```READ (x)``` ```` is inline code and quotes nothing after it; and a bare fence directly under
@@ -679,12 +667,12 @@ beside owner metadata, not only a matcher carried inside its `pattern` option.
679
667
  by one ASCII space. Target and scope form one resource selection; COPY/MOVE
680
668
  repeat the complete selection/metadata group per operand. ANTLR accepts
681
669
  adjacent slots and scope/metadata permutations within a selection without
682
- changing ownership or making them distinct canonical forms. Each selection
683
- has at most one scope; its metadata blocks retain their authored order. A second scope
684
- is refused, and the recovery reads the pair: a line number followed by its anchor
685
- (`<91> <@abcde>`, a copied row prefix) recovers to the anchor alone, two other single
686
- positions to one scope with both ends (`<@abcde> <-1>` to `<@abcde,-1>`), and anything
687
- else to one operation per selection.
670
+ changing ownership or making them distinct canonical forms. Metadata may also
671
+ precede the first target, belonging to that first selection. Between targets it
672
+ belongs to the preceding selection, never the following one. Single-target
673
+ scope-first tolerance remains valid with metadata before or after that scope.
674
+ Each selection has at most one scope; its metadata blocks retain their authored order. A second scope
675
+ is refused; the recovery is the scope's working form ({§parse-recovery}).
688
676
 
689
677
  §lifecycle-slots NOTE accepts no target, scope, or metadata. WAIT retains its
690
678
  optional target and duration under {§send-wait-scope}. Their literal bodies begin
@@ -714,14 +702,11 @@ are never split. Each URI inherits its slot's scope and metadata and compiles th
714
702
  BARE and NOTE take no scope, and neither does a SEND without a recipient; a `<…>` slot on such a
715
703
  heading, in any position, is skipped and the operation runs, with one warning-severity advisory
716
704
  naming the operation's slots and the dropped scope — `` `WORK` takes a target only; the scope
717
- `<1,-1>` was ignored. A scope selects lines in READ, EDIT and KILL. `` (`` `NOTE` takes no target or
705
+ `<1,-1>` was ignored. `` (`` `NOTE` takes no target or
718
706
  scope; … ``, `` `SEND` without a recipient takes no scope; … ``). An aside is never a scope, a
719
707
  recipient SEND keeps its scope for the recipient ({§send-directed-scope}), and WAIT follows
720
708
  {§send-wait-scope}. There is no ambiguity: the operation has one reading with or without
721
- the slot. Before this, the heading drew the grammar's expected-token diagnostic and the turn was
722
- dead; in the distinct recorded emissions one heading carried the form (zai run426,
723
- ```` ```WORK (worker://deprecation-implementer) <1,-1> ````), beside two recipient SENDs whose scope
724
- the recipient refuses at runtime.
709
+ the slot.
725
710
 
726
711
  §heading-inline-body Nonempty body text belongs below the fence header. Text after the slots
727
712
  on the heading line is read by the operation's shape, by the builder that reads that operation,
@@ -753,9 +738,8 @@ written on the heading line — under {§heading-inline-body}. The host names th
753
738
  (`ParseOptions.jsonBodyExecutors`). A heading that already carries a block keeps the object as
754
739
  inline body. On FIND, READ and KILL the same text is the matcher
755
740
  ({§naked-pattern}), because a search for JSON text is legitimate; a bare matcher
756
- that parses as a JSON object draws one advisory naming the option form ("`{…}`
757
- was read as the matcher; an option block is `[{…}]`."), so the collision is
758
- learned in the turn it happens.
741
+ that parses as a JSON object draws one advisory saying so: "`{…}` was read as the
742
+ matcher." ({§diagnostic-observation}).
759
743
 
760
744
  §operation-aside The final header modifier may be one single-line HTML
761
745
  comment. AstBuilder removes its delimiters and surrounding whitespace into
@@ -764,7 +748,7 @@ routing, timing, or body input. Comments inside a body remain literal except
764
748
  for the narrowly owned {§misplaced-aside-advisory}.
765
749
 
766
750
  §scheme-metadata-modifier A target may carry one single-line `[metadata]`
767
- block after its scope; executor and SEND fences also admit it without a target.
751
+ block, placed under {§slot-order}; executor and SEND fences also admit it without a target.
768
752
  The block belongs to the selected scheme or executor, which owns its shape,
769
753
  interpretation, validation and authority. The language assigns no meaning to
770
754
  the content and stores each block's exact inner text: balanced brackets inside
@@ -777,7 +761,9 @@ or multiline metadata loses its boundary.
777
761
  **House policy, not a language rule:** every first-party scheme and executor
778
762
  reads its block through the shared `MetadataOptions` reader, which takes the
779
763
  block with its brackets as a JSON array of option objects, merged left to
780
- right with later keys winning. A third-party owner may read its block any way
764
+ right with later keys winning. Reply SEND additionally owns a numeric completion
765
+ element ({§message-completion}); WAIT owns its numeric duration ({§send-wait-scope}).
766
+ A third-party owner may read its block any way
781
767
  it likes — the language guarantees only the exact inner text. Documentation
782
768
  for a first-party owner therefore shows the bracketed array form.
783
769
 
@@ -788,8 +774,8 @@ Worker; the shared reader withholds both from the owner's options.
788
774
 
789
775
  §matcher-option **`pattern` is the matcher, and it lives in the heading.** On
790
776
  FIND, READ, KILL, EDIT, and each COPY/MOVE operand, the option
791
- `[{"pattern": "<matcher>"}]` carries the matcher string exactly as a body once
792
- did: the leading prefix claims its dialect under {§matcher-prefix-claims}, and
777
+ `[{"pattern": "<matcher>"}]` carries the matcher string: the leading prefix
778
+ claims its dialect under {§matcher-prefix-claims}, and
793
779
  AstBuilder lifts it into the statement's `matcher` (`MatcherBody | null`),
794
780
  positioned dialect errors included. A block that carries only `pattern`
795
781
  leaves `metadata: null` for the owner; beside other keys the block stays with
@@ -798,10 +784,9 @@ language lifts only from one block that parses as a JSON array of objects;
798
784
  anything else lifts nothing and reaches the owner's `400` untouched. A
799
785
  `pattern` that is present but not a string is the language's own positioned
800
786
  diagnostic, as is a matcher of a claimed dialect that fails admission. FIND,
801
- READ, and targeted KILL take no body: a body beneath their heading is ignored and
802
- the operation still runs, with one warning-severity advisory naming the
803
- heading-line form (`FIND takes no body; the body was ignored. A pattern belongs
804
- on the opening fence line after the path.`); it is never silently read as a
787
+ READ, and targeted KILL take no body: a body beneath their heading is not used and
788
+ the operation still runs, with one warning-severity advisory naming the lines not
789
+ used (`FIND takes no body; lines 4–6 were not used.`); it is never silently read as a
805
790
  matcher, and it never strikes ({§matcher-body-redirect}). The heading line itself
806
791
  is the matcher's home ({§naked-pattern}). A body that is only an HTML comment is
807
792
  still the aside under {§misplaced-aside-advisory}. EDIT keeps its literal body:
@@ -838,10 +823,10 @@ governed by {§canonical-statement}; runtime conditions remain explicit below.
838
823
  | BARE | optional prompt resource | none | prompt; optional with a path |
839
824
  | WORK | optional fresh `worker://name`, or a prompt resource ({§worker-spawn-prompt-resource}) | none | prompt; optional with a resource |
840
825
  | FORK | optional context-inheriting `worker://name`, or a prompt resource | none | prompt; optional with a resource |
841
- | KILL | optional target; absent requests loop completion | optional text region on a target ({§kill-scope}) | literal final answer when parameterless; the distillation of what a `log:///` target retires ({§kill-scope}); none for other targets |
826
+ | KILL | required target | optional text region on a target ({§kill-scope}) | the distillation of what a `log:///` target retires ({§kill-scope}); none for other targets |
842
827
  | SEND | optional recipient | recipient-defined; none for workers ({§send-directed-scope}) | message |
843
828
  | NOTE | none | none | literal working memory |
844
- | WAIT | optional label ({§send-wait-scope}) | non-negative seconds | explanation of the wait |
829
+ | WAIT | optional label ({§send-wait-scope}) | none; duration is `[seconds]` metadata | explanation of the wait |
845
830
 
846
831
  §note-value NOTE retains its literal body as ordinary model-owned working memory.
847
832
  It has no target, scope, metadata, or lifecycle effect. Its full body participates
@@ -851,10 +836,10 @@ repetition and strike rules still apply.
851
836
 
852
837
  §reasoning-operations The shared fence parser admits complete, line-leading NOTE,
853
838
  FIND and READ statements from exposed provider reasoning. Admission is part of
854
- the language, not an optional compatibility mode. Other backtick or tilde
855
- code blocks are opaque; heading and missing-closer recovery cannot escape them or
856
- complete an unfinished reasoning operation. Blockquoted and inline examples are
857
- not headings. Other reasoned operations never execute.
839
+ the language. Other backtick or tilde code blocks are opaque; heading and
840
+ missing-closer recovery cannot escape them or complete an unfinished reasoning
841
+ operation. Blockquoted and inline examples are not headings. Other reasoned
842
+ operations never execute.
858
843
 
859
844
  Operations are admitted only after the provider response completes; their
860
845
  presence never interrupts generation. Selected operations precede the content
@@ -883,19 +868,18 @@ The path names a program or tool and is never split. Metadata such as
883
868
 
884
869
  §turn-disposition WAIT requests parking; its literal body does not control
885
870
  scheduling, and its optional target is the label the row keeps, never a join;
886
- the AST has no independently settable lifecycle status or metadata.
871
+ the AST retains the normalized metadata duration as `seconds`, separate from text coordinates.
887
872
  A turn admits any number of WAITs, all deferred until its other operations
888
873
  settle and together one park. End-of-program adjudication owns continuation, joining and completion
889
- under {§wait-obligation-matrix}. Parameterless KILL requests successful completion
890
- under {§kill-conclusion}; its optional body is the literal final answer.
891
- SEND delivers messages and NOTE retains memory, neither declaring an outcome.
874
+ under {§wait-obligation-matrix}. SEND carries optional message-completion metadata
875
+ under {§message-completion}. KILL requires a target; NOTE retains memory.
892
876
 
893
- §send-wait-scope WAIT's optional `<seconds>` is a non-negative finite scalar bounding
877
+ §send-wait-scope WAIT's optional `[seconds]` metadata is a non-negative finite scalar bounding
894
878
  this park, not the lifetime of its work. Without it, the service supplies its
895
879
  configured bound ({§worker-wait-timing}). Repeated duration slots select the
896
880
  smallest valid scalar. Zero continues without parking or a diagnostic. Invalid
897
- duration scopes are ignored with a terse warning; when none is valid, the configured
898
- bound applies. Metadata remains ignored without diagnostics.
881
+ duration metadata is ignored with a terse warning; when none is valid, the configured
882
+ bound applies. A text scope cannot supply a duration and receives the same warning.
899
883
  The optional target is a label, never a selected wake source;
900
884
  the body is the wait's explanation. Neither creates a schedule or restricts ordinary
901
885
  wake events. Ordinary malformed-header rules still apply. Multiple WAITs request
@@ -903,8 +887,7 @@ one park with the earliest bound, including the configured bound for each bare W
903
887
 
904
888
  §send-directed-scope A recipient SEND carries an optional numeric scope after
905
889
  its target and metadata through to the addressed owner, which assigns its
906
- semantics or refuses it; worker actors refuse one (`scope-unsupported`, 400),
907
- because later and recurring delivery belong to the schedule family. A
890
+ semantics or refuses it; worker actors refuse one (`scope-unsupported`, 400). A
908
891
  targetless message takes no scope. A scope never changes the message body or
909
892
  disposition.
910
893
 
@@ -914,7 +897,7 @@ anchored (```` ```KILL (log:///**/READ) <17,-1>``` ```` or
914
897
  selects rows or lines (```` ```KILL (log:///**) [{"pattern": "~stale"}]``` ````, {§matcher-option}).
915
898
  Each operand becomes an ordinary KILL statement with its own `target`, `lineMarker` and
916
899
  `matcher`; no grouped AST reaches execution ({§target-group}).
917
- The body is retained on parameterless KILL ({§kill-conclusion}) and on a `log:///` target, where
900
+ KILL requires a target. Its body is retained on a `log:///` target, where
918
901
  it is the model's distillation of what the KILL retires and lands as its own NOTE row
919
902
  ({§log-kill-distillation}); every other targeted KILL has `body: null`, and a body beneath one
920
903
  draws the advisory ({§matcher-body-redirect}). Beneath a `log:///` KILL the body is never read as
@@ -923,12 +906,10 @@ Without a scope, KILL retires or deletes the whole target; with one, it removes
923
906
  that span — of a log body's packet projection or of an entry's content. Core owns the
924
907
  one-way semantics: there is no operation that restores a scoped-away log body.
925
908
 
926
- §legacy-bracket-slot Brackets are the metadata modifier, never an executor
927
- selector: the runtime or MCP service is the fence name, and tool input belongs
928
- in the body. A bracket block that leads an executor fence or follows a target
929
- is metadata, so a legacy `[node]` selector reaches its owner as metadata text
930
- and is refused there; a bracket before the target of a non-executor OP is one
931
- bounded header diagnostic that selects nothing.
909
+ §bracket-metadata-slot Brackets are the owner's metadata modifier: the runtime
910
+ or MCP service is the fence name, and tool input belongs in the body. A bracket
911
+ block is metadata before or after the target, with ownership under {§slot-order};
912
+ its position does not select an executor or change its interpretation.
932
913
 
933
914
  The `<scope>` slot is optional where admitted and its domain is OP-specific. FIND
934
915
  scopes ordered results. Executions and SEND scope owner-defined timing. READ, EDIT, COPY,
@@ -985,7 +966,7 @@ same bare spelling the receipt used: a model that appends the channel to the pat
985
966
  shown addresses the same entry, and so does one that writes it after the parenthesis,
986
967
  `(page.html) #readable`, which {§naked-pattern} reads as the channel. `raw` is therefore always the path alone; a
987
968
  bare path never contains a literal `#`, and `PlurnkParser.stringify` renders the channel back.
988
- Without a `#` the field is absent, so an older `LocalPath` literal stays valid.
969
+ Without a `#` the field is absent.
989
970
 
990
971
  §read-exact-target READ targets one exact resource (a local path or scheme
991
972
  URL, with optional `#channel` fragment or `[metadata]`) and has no body. A
@@ -1203,7 +1184,7 @@ The operation column names the canonical AST operation after
1203
1184
  | COPY/MOVE destination | 0/1/2/4 text coordinates after target | Region replaced or insertion point at the destination |
1204
1185
  | KILL | 0/1/2 text coordinates | Whole target when absent; one physical line or inclusive range when present ({§kill-scope}) |
1205
1186
  | execution | None | Lifetime uses metadata; observation cadence belongs to the daemon ({§exec-lifetime}) |
1206
- | WAIT | Non-negative seconds | Maximum park duration ({§send-wait-scope}) |
1187
+ | WAIT | None | Maximum park duration uses metadata ({§send-wait-scope}) |
1207
1188
  | Directed SEND | Owner-defined numeric scope | Carried to the addressed owner; worker actors refuse it ({§send-directed-scope}) |
1208
1189
 
1209
1190
  Text coordinates use the algebra in {§text-scope-semantics}: one integer is a
@@ -1278,27 +1259,26 @@ rule protects code examples in SEND, WORK, FORK, BARE and every other body.
1278
1259
 
1279
1260
  ## 9. Turn dispositions
1280
1261
 
1281
- The runtime adjudicates a nonempty admitted program against actual messages,
1262
+ The runtime adjudicates each admitted turn against actual message resolutions,
1282
1263
  results and live obligations ({§wait-obligation-matrix}). A response with no
1283
- operation receives empty-turn recovery, not successful completion ({§empty-turn}).
1264
+ operation resolves no message and creates no new observation obligation ({§empty-turn}).
1284
1265
 
1285
1266
  | Intent | Nominal status | Meaning |
1286
1267
  |---|---|---|
1287
1268
  | Unobserved messages or results | 102 | Continue silently |
1288
- | No authored response operations or fresh operation/parser failure, without WAIT | 102 | Recover before automatic parking |
1289
- | WAIT | 202 | Park when a live obligation exists; otherwise continue at 102 |
1290
- | Eligible parameterless KILL ({§kill-conclusion}), live work remains, no fresh failure | 202 | Join the held work without delivering its body |
1291
- | Eligible parameterless KILL, messages and results observed, no held work | 200 | Conclude under {§kill-conclusion}; deliver a nonempty body, or finish silently without inventing or repeating a reply |
1292
- | Other admitted program | 102 | Continue regardless of earlier replies or live work |
1269
+ | No operations while messages remain unresolved, or fresh operation/parser failure, without WAIT | 102 | Recover before automatic parking |
1270
+ | WAIT with live obligations | 202 | Park with the supplied or configured duration |
1271
+ | Resolved messages, observed results, live work remains | 202 | Join the held work; replies remain delivered |
1272
+ | Resolved messages, observed results, no held work | 200 or 499 | Settle under {§loop-completion}, retaining the aggregate message outcome |
1293
1273
  | KILL own worker | 499 | Cancel unfinished work in that worker and its descendants |
1294
1274
  | Runtime or infrastructure failure | 5xx | Not a model-authored task status |
1295
1275
 
1296
1276
  ### The terminal contract (waitpid)
1297
1277
 
1298
- The model may supply WAIT or request completion with parameterless KILL. The host, not the grammar, owns
1278
+ The model may supply WAIT or complete message obligations with SEND metadata. The host, not the grammar, owns
1299
1279
  turn boundaries and adjudicates the loop's actual obligations. Asking
1300
- the human uses the native `question` executor tool when enabled ({§question-tool}), not a
1301
- disposition. The shape rules ARE structural:
1280
+ the human uses the native `question` executor tool, offered only when a person attends the
1281
+ worker's owner ({§owner-interaction-ring}), not a disposition. The shape rules ARE structural:
1302
1282
 
1303
1283
  - §send-mid-reservation WAIT is reserved ({§turn-disposition}).
1304
1284
  A turn admits any number of lifecycle declarations, anywhere among its
@@ -1309,19 +1289,17 @@ disposition. The shape rules ARE structural:
1309
1289
  dispositions until the other admitted operations settle
1310
1290
  ({§op-execution-order}). Nothing is dropped and no diagnostic is raised for
1311
1291
  position. Omission does not synthesize a disposition ({§turn-shape}).
1312
- This applies to WAIT, not to the literal answer region opened by parameterless KILL
1313
- ({§terminal-kill}).
1314
1292
  - SEND is communication: an optional recipient path and an optional body.
1315
1293
  - §park-202-only WAIT joins live work: an open stream or a live
1316
- child. With none, it continues. Its optional duration bounds parking ({§send-wait-scope});
1294
+ child. With none, it does not park. Its optional duration bounds parking ({§send-wait-scope});
1317
1295
  a future message is scheduled through the schedule family.
1318
1296
  - §lifecycle-only-turn A WAIT-, SEND-, NOTE-, or KILL-only turn is valid.
1319
1297
  NOTE does not request parking or acknowledge messages.
1320
1298
  Ordinary repetition, strike and execution limits still apply.
1321
1299
 
1322
- SEND with no `(path)` answers the open messages. SEND with `(path)` directs the
1323
- message to that recipient. Neither form concludes the loop. Only parameterless KILL
1324
- requests completion under {§kill-conclusion}; dispatch still executes the whole program.
1300
+ SEND with no `(path)` replies to open messages. SEND with `(path)` directs the
1301
+ message to that recipient. Completion metadata resolves message obligations
1302
+ ({§message-completion}); dispatch still executes the whole program.
1325
1303
 
1326
1304
  ### §send-body SEND body projection
1327
1305
 
@@ -1394,7 +1372,7 @@ the executor's own spelling. Quoted blocks, offset lines and names inside a sent
1394
1372
  §recorded-emissions **The parser is regressed against emissions models actually produced,
1395
1373
  not fixtures we wrote.** `test/fixtures/recorded-emissions.jsonl` holds one real exemplar
1396
1374
  of every distinct parse shape observed across the live and demo drills — the operations
1397
- authored, whether outside text appeared, how many parameterless KILLs appeared, and
1375
+ authored, whether outside text appeared, and
1398
1376
  the status the engine recorded at the time. A fixture encodes what we believe a model
1399
1377
  emits; a recording encodes what one did, so only recordings test the shapes models actually
1400
1378
  produce (#802, #809).
@@ -1562,12 +1540,19 @@ schema, this suffix invariant, and Problem status equality.
1562
1540
  | 100–399 | `problem` is forbidden |
1563
1541
  | 400–599 | One RFC 9457 `problem` object is required |
1564
1542
 
1565
- The legacy top-level `error` field is forbidden. Producer-specific success
1566
- fields and Problem Details extension members remain open. A malformed result is
1543
+ Producer-specific success fields and Problem Details extension members remain
1544
+ open. A malformed result is
1567
1545
  an internal producer contract violation; it is not converted into a second
1568
1546
  model-facing failure envelope.
1569
1547
 
1570
- ### 13.5 Problem Details
1548
+ ### 13.5 Diagnostics and Problem Details
1549
+
1550
+ §diagnostic-observation **A diagnostic says what happened, never why.** Every Problem `detail`
1551
+ and `recovery`, parse diagnostic and notice names what was read, what was done with it, and
1552
+ where, and may show the working form of the construct it read or refused. It never names a cause
1553
+ or an intent the input does not establish: no likely mistake, no reconstruction of what the author
1554
+ meant, no alternative the producer did not apply, and no form the language no longer has. Each
1555
+ sentence presupposes only what the producer itself decided.
1571
1556
 
1572
1557
  §problem-details `ProblemDetails` requires `type`, `title`, `status`, and `detail`;
1573
1558
  `instance` is optional until a durable host can attach the occurrence URI.
@@ -1585,8 +1570,8 @@ model-facing failure envelope.
1585
1570
  | extensions | Factual producer-known operands or constraints |
1586
1571
 
1587
1572
  `detail` is failure truth; `recovery` is not a second explanation. Producers do
1588
- not infer motives, blame the model, restate status, or expose an implementation
1589
- accident as the cause. General syntax and workflow teaching remain in the model
1573
+ not blame the model, restate status, or expose an implementation accident as the
1574
+ cause ({§diagnostic-observation}). General syntax and workflow teaching remain in the model
1590
1575
  packet rather than being repeated in every Problem.
1591
1576
 
1592
1577
  `Problems.create(owner, code, status, detail, extensions?, options?)` derives a
@@ -1630,6 +1615,12 @@ complete RFC 9457 object.
1630
1615
  failure truth, lifecycle, scheduling, or recovery. Sharing a renderer with
1631
1616
  Problems does not merge their semantics.
1632
1617
 
1618
+ §notice-directive A Notice's `message` states what happened ({§diagnostic-observation}).
1619
+ When the observation concerns the model's own output, the producer may attach one
1620
+ `directive`: `YOU MUST`, `YOU MUST NOT`, `YOU MUST ONLY`, `YOU SHOULD` or
1621
+ `YOU SHOULD NOT`, then the rule. An observation of the environment carries none.
1622
+ Clients display the message; the model packet leads with the directive.
1623
+
1633
1624
  ### §client-display-capabilities 13.7 Client display capabilities
1634
1625
 
1635
1626
  `ClientDisplayCapabilities` is the transport-neutral installed-capability
@@ -1760,21 +1751,25 @@ class PlurnkParseError extends Error {
1760
1751
  readonly source: ErrorSource;
1761
1752
  readonly severity: Severity;
1762
1753
  readonly recovery: string | undefined;
1754
+ readonly operationOmitted: boolean;
1763
1755
  }
1764
1756
  ```
1765
1757
 
1758
+ §parse-admission **A diagnostic's severity does not describe whether an operation ran.**
1759
+ `operationOmitted` identifies unexecuted unfenced operations, unknown operation-shaped
1760
+ tags and unrecovered native calls. A warning about a retained operation or a quoted
1761
+ example does not set it. This fact survives serialization when true; consumers do
1762
+ not infer it from diagnostic wording. It does not independently prescribe a strike.
1763
+
1766
1764
  §parse-recovery **Every hard diagnostic carries its working form.** A `severity: "error"`
1767
- diagnostic names, in `recovery`, the form that runs, in the model's terms: a refused heading
1768
- carries the operation's canonical line (`` `READ (path) <L,M>? pattern? <!-- aside -->?` on the
1769
- opening fence line; READ takes no body. ``), a refused matcher the dialect's form with an example,
1770
- and a refused regex the regex that matches the words the model wrote — `` A pattern is a regex:
1771
- write `/url/` to match lines containing url; `*` repeats what precedes it. To select files by
1772
- name, put the glob in the target: `FIND (tests/*url*)`. `` — where the glob suggestion is derived
1773
- from the pattern only when it is glob-shaped, and the regex sentence stands alone otherwise. The
1774
- runtime projects `recovery` as the Problem's `recovery` beside the verbatim `message`; an advisory
1775
- carries none, since its statement ran. A refused matcher carries the same pair into its own
1776
- operation's Problem ({§matcher-refusal}). Before this, a refused regex (`/*url*/`, DeepSeek run429)
1777
- reported `Nothing to repeat` with no way forward and the turn was spent.
1765
+ diagnostic names, in `recovery`, the form of the construct it refused, in the model's terms: a
1766
+ refused heading carries the operation's canonical line (`` `READ (path) <L,M>? pattern? <!-- aside -->?`
1767
+ on the opening fence line; READ takes no body. ``), a refused scope the scope's form, and a refused
1768
+ matcher its dialect's form with an example (`` A pattern is a regex written `/pattern/flags`, such
1769
+ as `/timeout/i`. ``). The working form is the language's, never a reconstruction of the input
1770
+ ({§diagnostic-observation}). The runtime projects `recovery` as the Problem's `recovery` beside the
1771
+ verbatim `message`; an advisory carries none. A refused matcher carries
1772
+ the same pair into its own operation's Problem ({§matcher-refusal}).
1778
1773
 
1779
1774
  §parser-position Parser source locations are points, not text regions. An AST
1780
1775
  statement's `position` identifies the first backtick of its header; a diagnostic
@@ -1837,9 +1832,8 @@ diagnostics are:
1837
1832
  refusal ({§matcher-refusal}).
1838
1833
  - §regex-sed-range **A sed line range is named as one.** A regex matcher written as a sed
1839
1834
  address range — `/a/,/b/` or `/a/,+N` — is refused ({§matcher-refusal}) as a range, never as invalid flags: the
1840
- diagnostic says a matcher selects only the lines it matches, gives the one regex that
1841
- locates the ends (`/a|b/`, or `/a/`) and the scope that then addresses the span
1842
- (`<first,last>`, or `<N,M>` with M being N plus the range's count).
1835
+ diagnostic names the range suffix and gives the regex matcher's working form.
1836
+ It does not synthesize a different selector or a workflow ({§diagnostic-observation}).
1843
1837
  - §unclosed-regex **A regex that never closes.** A `/pattern` matcher with no closing
1844
1838
  `/` is read as the whole pattern with no flags, with one warning-severity advisory
1845
1839
  naming the closing slash and the flag position. The reading is deterministic because
@@ -1870,15 +1864,9 @@ diagnostics are:
1870
1864
  `FIND (src/parser.ts) [{"pattern": "/\\bparse\\w+\\b/"}]`. `^` claims the regex
1871
1865
  dialect without slashes or flags: the whole text is the pattern, so
1872
1866
  `READ (notes.md) ^Decision:.*` selects lines beginning with `Decision:`.
1873
- `^` is deliberately absent from `plurnk.md`'s dialect table and belongs here instead
1874
- (#804). It is carried because `^` meaning "anchor" is among the
1875
- strongest instincts a model arrives with: it will write `^Decision:.*` whether or not it
1876
- was taught to, and the engine honours what it will reach for anyway. Teaching it would
1877
- spend hot-path weight on a line that changes no behaviour. The omission is therefore not
1878
- drift between the teaching and the engine, and closing it in either direction is a
1879
- regression: restoring the row pays for nothing, and retiring the prefix makes the
1880
- pretrained spelling a glob that matches a literal caret — silence in place of the
1881
- selection the model asked for.
1867
+ `^` is deliberately absent from `plurnk.md`'s dialect table: a model writes the anchor
1868
+ whether or not it is taught, so the engine honours it without spending hot-path teaching
1869
+ on it. The omission is not drift between the teaching and the engine.
1882
1870
  - §trailing-slots **Slots after the matcher peel off the right.** The heading text after
1883
1871
  the matcher is read backwards: a trailing `<!-- aside -->`, a trailing `<scope>` in the
1884
1872
  shapes the lexer admits (result positions on FIND, text coordinates elsewhere) and a
@@ -1916,9 +1904,9 @@ diagnostics are:
1916
1904
  - §bare-target **A target written without its parentheses is refused with the line that runs.**
1917
1905
  A FIND, READ or EDIT heading with no target whose heading text opens with a word that is no
1918
1906
  matcher sigil, `READ a.py <1,4>`, cannot run: the word stands where the target goes, but on FIND it
1919
- could as well be a pattern. The statement is one hard diagnostic that writes the corrected line —
1920
- `` `READ` has no target: `a.py` stands where the target goes. Write the target in parentheses:
1921
- `READ (a.py) <1,4>`. `` A targetless KILL's heading text remains its inline deliverable.
1907
+ could as well be a pattern. The statement is one hard diagnostic,
1908
+ `` `READ` has no target: `a.py` stands where the target goes. ``, whose recovery is the operation's
1909
+ working form ({§parse-recovery}).
1922
1910
  - §bare-anchor-scope **An EDIT's anchor without its angle brackets is its scope.** On an EDIT
1923
1911
  heading, `@abcde` or `@abcde,@fghij` standing alone where the scope goes — nothing but an aside, a
1924
1912
  closer or the line end after it — is the scope `<@abcde>`, with one warning-severity advisory:
@@ -1927,10 +1915,10 @@ diagnostics are:
1927
1915
  marker (14 distinct recorded headings). On READ and KILL the same text stays a literal matcher,
1928
1916
  since `@patch` is a search a model means.
1929
1917
  - §matcher-body-redirect **Unread body text on bodyless operations.** FIND, READ and bodyless
1930
- KILL keep the statement but discard text the matcher did not consume, with one warning:
1931
- `READ takes no body; the body was ignored. A pattern belongs on the opening fence line
1932
- after the path.` KILL's advisory says `This KILL`, limiting the claim to that invocation:
1933
- parameterless completion and log distillation bodies remain valid. Delivered like
1918
+ KILL keep the statement but do not use text the matcher did not consume, with one warning
1919
+ naming where it stands: `READ takes no body; lines 4–38 were not used.`, or `the text after its
1920
+ heading on line 3` when it shares the heading's line. KILL's advisory says `This KILL`, limiting the claim to that invocation:
1921
+ log distillation bodies remain valid. Delivered like
1934
1922
  {§misplaced-aside-advisory} as a `parse_advisory` notice (a warning, never an error).
1935
1923
  One sigil line beneath a bodyless heading is the bare form written a line low and still lifts;
1936
1924
  nothing else is promoted into a matcher from below the heading. An option block's `pattern`
@@ -1960,33 +1948,33 @@ diagnostics are:
1960
1948
  with any other content is ignored under the same advisory path
1961
1949
  ({§matcher-body-redirect}).
1962
1950
 
1963
- §error-shape The diagnostic class determines how much guidance the parser may
1964
- provide. Advisories belong to one successfully built statement. A rejected
1951
+ §error-shape The diagnostic class determines the parts of a message; every class says what
1952
+ happened, never why ({§diagnostic-observation}). Advisories belong to one successfully built statement. A rejected
1965
1953
  statement emits its hard diagnostic, not normalization advisories; neither a
1966
1954
  rejection nor an internal exception carries advisories into another statement
1967
1955
  or parser invocation.
1968
1956
 
1969
- | Class | Surface | Message contract |
1970
- |------------------------|-----------------------|-------------------------------------------------------------------------------------------|
1971
- | Hard fact | `severity: "error"` | One concise observed fact and violated constraint in PLURNK vocabulary. |
1972
- | Targeted hard redirect | `severity: "error"` | One canonical correction only when parser state makes the intended structure unambiguous. |
1973
- | Non-fatal advisory | `severity: "warning"` | One narrowly gated likely mistake and canonical alternative; input remains admitted. |
1974
- | Boundary loss | `unparsedTail` | Where trust ends, which header slot remains open, and why later input is undefined. |
1957
+ | Class | Surface | Message contract |
1958
+ |---------------|-----------------------|-------------------------------------------------------------------------------------------------------------------|
1959
+ | Hard fact | `severity: "error"` | What was read, where, and the constraint it violates, in PLURNK vocabulary; `recovery` is the construct's working form ({§parse-recovery}). |
1960
+ | Advisory | `severity: "warning"` | What was read and what was done with it, read as a named construct or not used, and where; the statement remains admitted. |
1961
+ | Boundary loss | `unparsedTail` | Where trust ends and which header slot remains open; later input is undefined. |
1975
1962
 
1976
1963
  All messages use PLURNK protocol vocabulary: opening fence, closing fence, target,
1977
1964
  scope, line marker, body, section boundary, or space between slots. They never
1978
1965
  expose ANTLR rule or token names. They refer to a slot or
1979
- feature rather than an implementation rule. Generic tutoring, speculative
1980
- intent, coordinate restatement, and multiple repair strategies are forbidden.
1966
+ feature rather than an implementation rule. Generic tutoring, coordinate
1967
+ restatement and multiple repair strategies are forbidden, and no message names an
1968
+ intent ({§diagnostic-observation}).
1981
1969
  Unexpected top-level text immediately after a closed operation identifies that
1982
1970
  operation's opening line, closing line, and matching backtick count.
1983
1971
 
1984
1972
  Examples of canonical hard facts:
1985
1973
 
1986
1974
  - `unrecognized character '<' in target`
1987
- - `unexpected bracket modifier; the fence name selects the executor`
1975
+ - `unrecognized character '[' in the sh header`
1988
1976
  - `unrecognized character 'X' in statement header`
1989
- - `WAIT's body begins below the header`
1977
+ - `unrecognized character '[' in the WAIT header`
1990
1978
  - `expected ')'; got ':'`
1991
1979
 
1992
1980
  Each malformed statement produces at most one hard error. The first recorded