@plurnk/plurnk-contracts 1.29.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.env.defaults CHANGED
@@ -7,5 +7,5 @@ PLURNK_HOST=127.0.0.1
7
7
  # The daemon's TCP port, shared by all client interfaces.
8
8
  PLURNK_PORT=1066
9
9
  # The whole URL a client dials instead: a remote portal, or a daemon bound to an address no
10
- # client can dial (0.0.0.0). Unset = http://$PLURNK_HOST:$PLURNK_PORT.
10
+ # client can dial (0.0.0.0). Unset = http://$PLURNK_HOST:$PLURNK_PORT/agui.
11
11
  # PLURNK_AGUI_URL=
package/SPEC.md CHANGED
@@ -10,7 +10,7 @@ runtime-neutral wire envelopes; `@plurnk/plurnk-parser` implements the language
10
10
  | Surface | Canonical export or artifact |
11
11
  | ------------------------------------------------------------------------------- | --------------------------------------------------- |
12
12
  | AST, validators, Problems, results, Notices, text regions and extents | `@plurnk/plurnk-contracts` |
13
- | Capability and loop policies | `CapabilityPolicy`, `LoopPolicy`, `LoopPolicyRequest`, `PROPOSAL_POLICIES` |
13
+ | Capability admission and approval ownership | `CapabilityPolicy`, `WorkerOwner`, `ProposalPolicy`, `PROPOSAL_POLICIES` |
14
14
  | Durable effort | `Effort`, `EFFORTS` |
15
15
  | Model route and catalog discovery | `ModelRoute`, `ModelCatalogQuery`, `ModelCatalogPage`, `ModelReadiness` |
16
16
  | Stopped-world client contract | `ProposalDisposition`, `ProposalProjection` |
@@ -137,9 +137,10 @@ 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
- | `LoopPolicy` | One `review`, `accept`, or `reject` proposal disposition | Snapshot once when the loop is created |
141
- | `ProposalDisposition` | Client authority, or the loop's exact automatic accept/reject | Compute precedence from effective loop policy, proposal kind, and stale-target truth |
142
- | `ProposalProjection` | Identity, `{ scheme, authority, pathname }` review target, body/attrs, effective policy, stale signal, disposition | Derive one validated projection for live delivery and durable reconnect discovery |
140
+ | `WorkerOwner` | Nonempty address and unique client-tool names | Persist by workspace; inherit through worker creation |
141
+ | `ProposalPolicy` | `review`, `accept`, or `reject` | Read the server's disposition knob at the proposal boundary |
142
+ | `ProposalDisposition` | Review, or automatic accept/reject with optional outcome | Select once for the pending gate |
143
+ | `ProposalProjection` | Identity, review target, body/attrs, owner, stale signal, disposition | One validated projection for live delivery and reconnect |
143
144
  | `ProviderUsage` | Conventional input/output totals with cache and reasoning details | Preserve observed quantities without replacing absence with zero |
144
145
  | `ProviderCost` | Exact charged, estimated, or unknown monetary evidence | Normalize one monetary disposition for each physical provider request |
145
146
  | `ProviderRequestAccounting` | Usage and cost evidence for one physical provider request | Preserve request order across retries, failover, success, and failure |
@@ -167,15 +168,11 @@ workspace layer and their normalized intersection: `service`, `workspace`, and
167
168
  capability policy or inherited bound; every actor uses the same live workspace
168
169
  policy. A client never derives effective authority from the mutable layer alone.
169
170
 
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.
171
+ §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
+ message-carried authority. Core owns persistence, inheritance and settlement
175
+ ({§worker-ownership}, {§proposal-disposition}); capability admission precedes approval.
179
176
 
180
177
  §effort-wire `Effort` is exactly `off | adaptive | low |
181
178
  medium | high | xhigh | max`. The schema owns this shared wire vocabulary. Providers own the
@@ -314,7 +311,8 @@ column-zero line outside any block containing a native operation's name and at m
314
311
  `<!-- aside -->`, with only horizontal whitespace otherwise, opens that operation as if it were
315
312
  fenced with the taught three backticks. The aside retains its ordinary meaning: no target or
316
313
  modifiers are inferred. Its body runs to a line that is exactly the name
317
- again, to the next heading ({§fence-heading-in-body}), or to the end of the turn. A fence inside
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
318
316
  naming nothing known is body; the block expects no closer, so its body is never cut
319
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 —
320
318
  `` `KILL` opened with no fence; the taught form is three backticks. `` One rule for every native
@@ -330,8 +328,8 @@ read this way.
330
328
  §fence-heading-in-body Outside a complete nested block ({§balanced-fences}), a fence
331
329
  line of three or more backticks and a name that is a native operation or a known executor
332
330
  is a heading. Inside an open block it either ends that block without closing it and opens the
333
- next statement, or it is a literal example held by the block; the block holds it only while
334
- the block ends at a real closer, and {§fence-pairing} chooses between the two. Fence lines of
331
+ 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
335
333
  fewer than three backticks are never headings ({§operation-fences}). Known executors are `sh`
336
334
  plus what the host names in `ParseOptions.executors`. Inside a parameterless `KILL` a
337
335
  heading never ends the block ({§terminal-kill}).
@@ -364,7 +362,9 @@ input at once by {§fence-pairing}:
364
362
 
365
363
  A whole-input pass, `FencePairing`, decides where every block ends before the lexer reads a
366
364
  token; the lexer's semantic predicates consult its decision, and ANTLR keeps framing, slots and
367
- statement composition ({§parser-architecture}). The pass is a least-cost parse of the fence
365
+ statement composition ({§parser-architecture}). Heading shape comes from the same grammar and
366
+ AST interpretation as normal admission, including recovered operand order and target notation;
367
+ fence pairing does not infer slot ownership from raw text. The pass is a least-cost parse of the fence
368
368
  lines as a bracket language, in the manner of Aho and Peterson's least-errors parser
369
369
  (SIAM J. Comput. 1(4), 1972): every reading of the fences is a derivation, and the pass returns
370
370
  the cheapest.
@@ -409,7 +409,7 @@ repaired reading: a complete nested interpretation takes precedence. Each class
409
409
  | Counted | Complete readings | Repaired readings | Why |
410
410
  |---|---|---|---|
411
411
  | Hidden operations: a heading that will not run, written at its block's width or wider | 1, together with the next row | 1 | Every operation the author wrote should run |
412
- | Repairs: a supplied closer (except a message's run to the end of the input, {§message-run-on}), a fence read as a stray, a narrower closer accepted, a body under an operation that takes none (FIND, READ, COPY, MOVE, targeted KILL), a body not well-formed in the media type its runtime declares ({§executor-invocation}) | none by definition | 2 | The least-errors distance |
412
+ | Repairs: a supplied closer (except a message's run to the end of the input, {§message-run-on}), a fence read as a stray, a narrower closer accepted, a body under an operation that takes none (FIND, READ, COPY, MOVE, non-log targeted KILL), a body not well-formed in the media type its runtime declares ({§executor-invocation}) | none by definition | 2 | The least-errors distance |
413
413
  | Literal by declaration: a heading narrower than its block, or no wider than the labeled block or example holding it, or inside a quotation or a KILL, or an executor heading inside a SEND, WORK, FORK or BARE body ({§prose-code-blocks}); a labeled fence in a body read as text | 1, together with the row above | 3 | Every code block the author declared should stand |
414
414
  | Supplied closers | 2 | 4 | A block the author opened should end at a fence the author wrote |
415
415
  | Other fences read as text | 3 | 5 | The least departure from the fences as written |
@@ -443,11 +443,13 @@ heading. An operation example quoted in a body of its own width therefore reads
443
443
  closing and the example running; the wider outer fence or the tab offset quotes it
444
444
  ({§operation-fences}).
445
445
 
446
- §terminal-kill **A KILL body is the deliverable.** Inside a parameterless `KILL`, at any depth, a
447
- heading is a literal example or text and never ends the block; the block ends at its closer or
448
- the end of the input. Of 796 recorded parameterless KILLs, 9 were followed by any other
449
- operation, while 62 of 120 SENDs were, so the rule is KILL's alone. It keeps a final report whole
450
- and never runs a command the report only shows.
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}.
451
453
 
452
454
  §prose-code-blocks **A message or a prompt shows code.** Inside a `SEND`, `WORK`, `FORK` or `BARE`
453
455
  body, at any depth, a heading that names an executor (` ```sh `, ` ```python3 `) is a code block the
@@ -458,8 +460,8 @@ operation. In the distinct recorded benchmark emissions an executor heading ende
458
460
  run218, run114 and run118, deepdumb run44) was the child's task, the prompt's code or code quoted in
459
461
  a report, and reading it as an operation truncated the task and ran the example in the parent.
460
462
 
461
- §message-run-on **A message runs to the end of the turn.** A parameterless `KILL`, or a `SEND`,
462
- `WORK`, `FORK` or `BARE`, whose block reaches the end of the input without its closer takes it
463
+ §message-run-on **A message runs to the end of the turn.** A `SEND`, `WORK`, `FORK` or `BARE`
464
+ whose block reaches the end of the input without its closer takes it
463
465
  there with no repair: the text after its last inner block is still the deliverable, the message
464
466
  or the task, never outside text. The run counts as a supplied closer, so a reading that closes at
465
467
  a real fence still wins, and it never costs an operation: when the reading that runs on hides more
@@ -468,11 +470,9 @@ operations the author wrote than the best repaired reading, the repaired reading
468
470
  inner fence keep what followed it (the qflash run192, run90 and run210 deliverables regain their last
469
471
  sections), one echoed transcript (glm run155) runs one more operation, and none loses one.
470
472
 
471
- §naked-kill **A naked KILL is the whole rest of the turn.** A parameterless `KILL` opened without
472
- a fence ({§naked-operation}), with or without an aside, is a completion: every fenced
473
- block inside it, a native operation heading included, is text the deliverable shows, exactly as
474
- inside a fenced KILL ({§terminal-kill}), and the block ends only at its name alone on a line or at
475
- the end of the input. No operation the deliverable shows runs.
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
476
 
477
477
  §pairing-witness **Witnesses.**
478
478
 
@@ -654,9 +654,9 @@ normalize optional bodies to null. Closing fences are conventional, never requir
654
654
  ({§closer-fallback}).
655
655
 
656
656
  §statement-rendering `PlurnkParser.stringify` renders native OP names and named
657
- runtime fences from the shared AST, with one blank line between operations; a
658
- target group renders every member with its own scope and metadata, then the
659
- group's naked pattern ({§target-group}).
657
+ runtime fences from the shared AST, with one blank line between operations. A
658
+ target group compiles to ordinary statements, each rendered with its resolved
659
+ selection ({§target-group}).
660
660
  Every closing fence occupies its own line, including bodyless operations;
661
661
  inline fences remain accepted input, not generated examples.
662
662
  It chooses at least three backticks and more than any run within the body
@@ -692,16 +692,24 @@ below the header.
692
692
 
693
693
  §target-group **READ and KILL take a target group.** A READ or KILL heading may carry several
694
694
  `(path)` slots, each binding the scope and metadata that follow it under {§slot-order}; a naked
695
- pattern on the heading ({§naked-pattern}) is the group's and applies to every member. The AST keeps
696
- the first slot in `target`, `lineMarker` and `metadata` and, only when the heading carries more than
697
- one slot, repeats it with the rest as `group: ResourceSelection[]` in authored order, each member's
698
- `matcher` the one its own metadata carried. The operation runs once per member, in authored order,
699
- under the core SPEC's {§safe-uri-target-groups}; a distilling body ({§log-kill-distillation}) lands
695
+ pattern on the heading ({§naked-pattern}) supplies the default for each member without its own
696
+ metadata `pattern`. The parser compiles the complete heading into one ordinary statement per
697
+ member, in authored order, each with its own target, scope, metadata and resolved matcher. No
698
+ duplicated first-member or group representation crosses the parser boundary. Every resulting
699
+ statement retains the heading's source position and aside; a heading-level parse error refuses
700
+ all its members, not valid sibling headings. A distilling body ({§log-kill-distillation}) lands
700
701
  once, with the first member. COPY and MOVE keep their two operands with their own meaning
701
702
  ({§transfer-resource-selections}); every other operation takes one slot, and a second slot on its
702
703
  heading is the slot-opener diagnostic. The form is accepted, not taught: it rescues a bulk attempt
703
704
  written one path per slot.
704
705
 
706
+ §safe-uri-target-groups **An explicit URI list is a target group.** In READ and KILL only,
707
+ one path slot may contain several explicit `scheme://` URIs separated by top-level commas or
708
+ whitespace; braces protect their contents. Every member must independently parse as a URI,
709
+ otherwise the target stays singular and exact. Local filenames containing spaces or commas
710
+ are never split. Each URI inherits its slot's scope and metadata and compiles through
711
+ {§target-group}; COPY/MOVE and other operations do not admit this tolerance.
712
+
705
713
  §scope-on-scopeless **A scope on an operation that takes none is dropped, and named.** WORK, FORK,
706
714
  BARE and NOTE take no scope, and neither does a SEND without a recipient; a `<…>` slot on such a
707
715
  heading, in any position, is skipped and the operation runs, with one warning-severity advisory
@@ -715,21 +723,33 @@ dead; in the distinct recorded emissions one heading carried the form (zai run42
715
723
  ```` ```WORK (worker://deprecation-implementer) <1,-1> ````), beside two recipient SENDs whose scope
716
724
  the recipient refuses at runtime.
717
725
 
718
- §heading-inline-body Nonempty body text belongs below the fence header.
719
- The ingester tolerates body text after horizontal whitespace on the header,
720
- preserves it, and emits one warning stating that normalization. This does not
721
- change the meaning of a compact empty block or permit unmatched fences.
726
+ §heading-inline-body Nonempty body text belongs below the fence header. Text after the slots
727
+ on the heading line is read by the operation's shape, by the builder that reads that operation,
728
+ and that reading is named: no text on the line is dropped silently, and none loses the operation.
729
+ Taken as the body, it is the first body line, with one warning: "`EDIT` body text was on the OP
730
+ line and was taken as the body; body content goes immediately beneath the opening fence line." A
731
+ sigil claims a matcher only on an operation that has one. This does not change the meaning of a
732
+ compact empty block or permit unmatched fences.
733
+
734
+ | Operation | Text after the slots on the heading line |
735
+ |---|---|
736
+ | FIND, READ, KILL with a non-log target | The matcher ({§naked-pattern}). Beside an option block's `pattern`, ignored ({§matcher-body-redirect}). |
737
+ | KILL on a log target | The matcher. Beside an option block's `pattern`, the first line of the distillation ({§log-kill-distillation}). |
738
+ | EDIT | A sigil-led matcher ({§edit-pattern}), unless an option block carries the `pattern`; otherwise the body. |
739
+ | COPY, MOVE | Ignored ({§transfer-resource-selections}). |
740
+ | SEND, WORK, FORK, BARE, executors | One JSON object is the option block where the heading admits one ({§bare-option-object}); otherwise the body. |
741
+ | NOTE, WAIT, KILL without a target | The body. |
722
742
 
723
743
  §bare-option-object A bare option object is the option block, where the house option array owns the
724
744
  block. On an operation that takes an option block and no bare matcher — SEND, BARE, WORK, FORK and
725
- every executor fence — heading text after the slots that is exactly one JSON object `{…}` is read
745
+ every executor fence — heading text after the slots that is exactly one JSON object `{…}`, where the
746
+ heading admits a block (BARE, WORK and FORK only after their target), is read
726
747
  as `[{…}]`: the AST carries the array form, the written heading renders it, and one
727
748
  warning-severity receipt names the indulgence in place of the {§heading-inline-body} advisory:
728
749
  "`SEND` took a bare option object; the taught form is `[{…}]`." The option array is a house
729
750
  convention, not a language rule ({§scheme-metadata-modifier}): an executor whose declared body is
730
751
  JSON ({§executor-invocation}), an MCP tool, reads the object as its body instead — its arguments,
731
- written on the heading line — with one warning: "`gitea` took its body on the heading line; the
732
- body belongs on the lines below it." The host names those executors to the parse
752
+ written on the heading line — under {§heading-inline-body}. The host names those executors to the parse
733
753
  (`ParseOptions.jsonBodyExecutors`). A heading that already carries a block keeps the object as
734
754
  inline body. On FIND, READ and KILL the same text is the matcher
735
755
  ({§naked-pattern}), because a search for JSON text is legitimate; a bare matcher
@@ -788,7 +808,7 @@ still the aside under {§misplaced-aside-advisory}. EDIT keeps its literal body:
788
808
  with a matcher it is the replacement for every selected span ({§edit-pattern}),
789
809
  and an absent body deletes them. `PlurnkParser.stringify` writes a lifted matcher
790
810
  whose block left no metadata back bare when the bare form reads back identically
791
- ({§naked-pattern}), otherwise as `[{"pattern": "…"}]`.
811
+ and draws no advisory ({§naked-pattern}), otherwise as `[{"pattern": "…"}]`.
792
812
 
793
813
  ## 3. Lexical elements
794
814
 
@@ -892,8 +912,8 @@ disposition.
892
912
  anchored (```` ```KILL (log:///**/READ) <17,-1>``` ```` or
893
913
  ```` ```KILL (worker:///notes.md) <@aB3dE,@0Aa9Z>``` ````), and an optional matcher option that
894
914
  selects rows or lines (```` ```KILL (log:///**) [{"pattern": "~stale"}]``` ````, {§matcher-option}).
895
- The AST is `{ op: "KILL", target, lineMarker: TextLineMarker | null, matcher: MatcherBody | null, body: string | null }`,
896
- with `group` beside them when the heading carries more than one slot ({§target-group}).
915
+ Each operand becomes an ordinary KILL statement with its own `target`, `lineMarker` and
916
+ `matcher`; no grouped AST reaches execution ({§target-group}).
897
917
  The body is retained on parameterless KILL ({§kill-conclusion}) and on a `log:///` target, where
898
918
  it is the model's distillation of what the KILL retires and lands as its own NOTE row
899
919
  ({§log-kill-distillation}); every other targeted KILL has `body: null`, and a body beneath one
@@ -985,8 +1005,10 @@ Mutation semantics:
985
1005
  - `<SL,SC,EL,EC>` deletes the exact exclusive-end region and inserts the body at its start.
986
1006
  - §transfer-resource-selections COPY and MOVE require two singular
987
1007
  `ResourceSelection` operands, source first and destination second, and admit
988
- no body. Each selection binds its own target, scope, matcher, and metadata
989
- under {§slot-order}, {§matcher-option}, and {§scheme-metadata-modifier}.
1008
+ no body: text after the slots on the heading line, or beneath it, is ignored and the
1009
+ operation still runs, with one warning-severity advisory (`MOVE takes no body; the body
1010
+ was ignored.`). Each selection binds its own target, scope, matcher, and metadata under
1011
+ {§slot-order}, {§matcher-option}, and {§scheme-metadata-modifier}.
990
1012
 
991
1013
  ### Per-operation observations
992
1014
 
@@ -1112,7 +1134,8 @@ refusal ({§matcher-refusal}), not a parse error.
1112
1134
  operation, two for COPY and MOVE, any number on READ and KILL ({§target-group}) — is a parser
1113
1135
  error at its opening paren. Report the unexpected slot and the grammar's expected
1114
1136
  alternatives when available, without inferring pattern intent or imposing another
1115
- operation's operand count. The statement is dropped and its siblings run.
1137
+ operation's operand count. Recovery-only body text is not advertised as a valid body
1138
+ for an operation that takes none. The statement is dropped and its siblings run.
1116
1139
 
1117
1140
  | Prefix | Dialect | Canonical form | Typed admission | Runtime owner |
1118
1141
  |-----------|----------|--------------------------------------|-----------------------------------|---------------------|
@@ -1274,7 +1297,7 @@ operation receives empty-turn recovery, not successful completion ({§empty-turn
1274
1297
 
1275
1298
  The model may supply WAIT or request completion with parameterless KILL. The host, not the grammar, owns
1276
1299
  turn boundaries and adjudicates the loop's actual obligations. Asking
1277
- the human is the native `question` executor tool ({§question-tool}), not a
1300
+ the human uses the native `question` executor tool when enabled ({§question-tool}), not a
1278
1301
  disposition. The shape rules ARE structural:
1279
1302
 
1280
1303
  - §send-mid-reservation WAIT is reserved ({§turn-disposition}).
@@ -1286,6 +1309,8 @@ disposition. The shape rules ARE structural:
1286
1309
  dispositions until the other admitted operations settle
1287
1310
  ({§op-execution-order}). Nothing is dropped and no diagnostic is raised for
1288
1311
  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}).
1289
1314
  - SEND is communication: an optional recipient path and an optional body.
1290
1315
  - §park-202-only WAIT joins live work: an open stream or a live
1291
1316
  child. With none, it continues. Its optional duration bounds parking ({§send-wait-scope});
@@ -1356,18 +1381,15 @@ tiers ignore outside text. Core alone owns storing it as the turn's outside sour
1356
1381
  and no-operation strikes ({§empty-turn}); parsing never infers delivery or completion
1357
1382
  intent.
1358
1383
 
1359
- §unfenced-operation **An operation written without its fence did not run, and the parser says
1360
- so.** An outside-text line that opens at column zero with an operation's name and anything else
1361
- — `KILL The answer…`, `READ (a.md)`, `KILL (notes.md)` — draws one warning: `` `KILL` has no
1362
- fence, so it did not run. `` The line is not response text ({§response-text}): it is neither stored
1363
- as outside text nor echoed into the next packet, and the exact emission remains at `ops://`; a name
1364
- with only an optional complete aside opens the operation instead ({§naked-operation}). A registered executor's name followed by an
1365
- operand slot — `gitea (list_issues)`, `sh(build.sh)` — draws the same warning under the executor's
1366
- own spelling; quoted blocks, offset lines and names inside a sentence draw nothing, since `sh`,
1367
- `env` and `members` are ordinary words. The model that wrote it believes it ran: stored as
1368
- outside text ({§outside-text}), an unfenced KILL would sit in the record as an answer never given,
1369
- and only the warning tells the model otherwise. The warning and the exclusion together keep the
1370
- line out of the record.
1384
+ §unfenced-operation **The parser identifies ignored unfenced fragments.** An outside-text line
1385
+ that opens at column zero with an operation's name and anything else — `KILL The answer…`,
1386
+ `READ (a.md)`, `KILL (notes.md)` — draws one warning: `` Unfenced `KILL` ignored. ``
1387
+ This describes only that fragment, never a fenced operation elsewhere in the turn or its reasoning.
1388
+ The line is not response text ({§response-text}): it is neither stored as outside text nor echoed
1389
+ into the next packet, and the exact emission remains at `ops://`. A name with only an optional
1390
+ complete aside opens the operation instead ({§naked-operation}). A registered executor's name
1391
+ followed by an operand slot — `gitea (list_issues)`, `sh(build.sh)` — draws the same warning under
1392
+ the executor's own spelling. Quoted blocks, offset lines and names inside a sentence draw nothing.
1371
1393
 
1372
1394
  §recorded-emissions **The parser is regressed against emissions models actually produced,
1373
1395
  not fixtures we wrote.** `test/fixtures/recorded-emissions.jsonl` holds one real exemplar
@@ -1437,7 +1459,7 @@ among them: the parser is `@plurnk/plurnk-parser`'s ({§parser-consumers}).
1437
1459
  | `TurnDisposition` | The one turn disposition and its recognition | {§turn-disposition} |
1438
1460
  | `CapabilityAdmission` | Admission of a capability descriptor against policy layers | {§capability-admission} |
1439
1461
  | `PLURNK_OPS`, `INTERNAL_ROW_OPS`, `PLURNK_FENCE` | The closed operation alphabet and the language's fence | {§canonical-statement} |
1440
- | `PROPOSAL_POLICIES` | The vocabulary a loop policy chooses from | {§loop-policy} |
1462
+ | `PROPOSAL_POLICIES` | Server proposal disposition vocabulary | {§proposal-policy} |
1441
1463
  | `WORKER_NAME` | Authority minting predicate | {§worker-name} |
1442
1464
  | `UNKNOWN_POSITION` | Frozen sentinel for an AST statement without retained parsed source | {§parser-position} |
1443
1465
 
@@ -1620,8 +1642,8 @@ discriminated values:
1620
1642
  | `mimetype` | Non-empty `mimetype` media type | `CapabilityDisplay` |
1621
1643
 
1622
1644
  `CapabilityDisplay` is a closed object whose optional `glyph` is a non-empty,
1623
- opaque string. Capability frameworks own and validate their intrinsic
1624
- declarations; Core owns composition of the installed families; interface
1645
+ opaque string. Frameworks own and validate their intrinsic
1646
+ declarations; Core owns composition of the installed extensions; interface
1625
1647
  modules expose this exact shape; clients own rendering, font support, theme,
1626
1648
  and identity fallback when `glyph` is absent. Empty framework sentinels are
1627
1649
  normalized to absence at composition. Display metadata is client state, never
@@ -1865,7 +1887,9 @@ diagnostics are:
1865
1887
  until what remains is the matcher. `READ (a.rs) /fn resolve_/ <1,-1> <!-- entities -->`
1866
1888
  is the same operation as `READ (a.rs) <1,-1> /fn resolve_/ <!-- entities -->`, with
1867
1889
  one warning-severity advisory naming the canonical order for the scope or block:
1868
- the grammar swallows up anything that passes as legitimate plurnk. A matcher that
1890
+ recovery requires a single possible path-slot owner. With multiple slots, a trailing scope
1891
+ or metadata block refuses the heading rather than relocating or discarding the slot.
1892
+ A matcher that
1869
1893
  itself ends in one of those shapes takes the option escape.
1870
1894
  `plurnk.md` teaches one order — `OP (path)? <scope|range>? [metadata]? pattern?
1871
1895
  <!-- aside -->?` — and free ordering is not taught. Small departure, small
@@ -1902,15 +1926,15 @@ diagnostics are:
1902
1926
  body instead, the anchor would be written into the file, or the EDIT refused for want of a line
1903
1927
  marker (14 distinct recorded headings). On READ and KILL the same text stays a literal matcher,
1904
1928
  since `@patch` is a search a model means.
1905
- - §matcher-body-redirect **A body beneath those headings.** Text below the heading
1906
- of a FIND, READ or targeted KILL is a body, and those operations take none: the builder
1907
- keeps the statement without it and raises one warning-severity advisory (`READ
1908
- takes no body; the body was ignored. A pattern belongs on the opening fence line
1909
- after the path.`). KILL's advisory names `KILL with a target`; it must not
1910
- prohibit parameterless completion bodies. Delivered like {§misplaced-aside-advisory} as a
1911
- `parse_advisory` notice (a warning, never an error). One sigil line beneath the heading is the bare form
1912
- written a line low and still lifts; nothing else is promoted into a matcher from
1913
- below the heading, and the advisory never echoes the body.
1929
+ - §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
1934
+ {§misplaced-aside-advisory} as a `parse_advisory` notice (a warning, never an error).
1935
+ One sigil line beneath a bodyless heading is the bare form written a line low and still lifts;
1936
+ nothing else is promoted into a matcher from below the heading. An option block's `pattern`
1937
+ prevents that lift, and the advisory never echoes the discarded text.
1914
1938
  - §combined-anchor-tolerance **Combined line number and anchor in a scope.** A
1915
1939
  text-coordinate scope written `L<@hash>` — digits immediately before the opener — is the
1916
1940
  displayed `42<@abcde>` row prefix copied whole: the scope is the anchor, the number is dropped, and one
@@ -11,7 +11,7 @@
11
11
  "loopId": { "type": "integer", "minimum": 1 },
12
12
  "turnId": { "type": "integer", "minimum": 1 },
13
13
  "sequence": { "type": "integer", "minimum": 1 },
14
- "origin": { "enum": ["model", "client", "_plurnk", "plugin"] },
14
+ "origin": { "enum": ["model", "client", "_plurnk"] },
15
15
  "projectRoot": { "type": ["string", "null"] },
16
16
  "statement": { "$ref": "https://schemas.plurnk.xyz/v0/PlurnkStatement.json" },
17
17
  "phase": { "enum": ["started", "settled"] },
@@ -4,13 +4,14 @@
4
4
  "title": "ClientInteractionProjection",
5
5
  "description": "The runtime-neutral client-interface projection of one pending client interaction. Live delivery and reconnect discovery expose this same shape.",
6
6
  "type": "object",
7
- "required": ["interactionId", "workerId", "loopId", "turnId", "request"],
7
+ "required": ["interactionId", "workerId", "loopId", "turnId", "recipient", "request"],
8
8
  "additionalProperties": false,
9
9
  "properties": {
10
10
  "interactionId": { "type": "integer", "minimum": 1 },
11
11
  "workerId": { "type": "integer", "minimum": 1 },
12
12
  "loopId": { "type": "integer", "minimum": 1 },
13
13
  "turnId": { "type": "integer", "minimum": 1 },
14
+ "recipient": { "type": "string", "minLength": 1 },
14
15
  "request": { "$ref": "https://schemas.plurnk.xyz/v0/ClientInteractionRequest.json" }
15
16
  }
16
17
  }
@@ -186,14 +186,6 @@
186
186
  "target": {
187
187
  "$ref": "#/$defs/PathOrNull"
188
188
  },
189
- "group": {
190
- "type": "array",
191
- "minItems": 2,
192
- "items": {
193
- "$ref": "https://schemas.plurnk.xyz/v0/ResourceSelection.json"
194
- },
195
- "description": "The heading's target group ({§target-group}): every `(path)` slot with the scope and metadata that follow it, in authored order, the first repeating `target`, `lineMarker` and `metadata`. Present only when the heading carries more than one slot; absent, as in every statement stored before groups existed, the operation has its one target."
196
- },
197
189
  "lineMarker": {
198
190
  "$ref": "#/$defs/TextLineMarkerOrNull"
199
191
  },
@@ -454,14 +446,6 @@
454
446
  "target": {
455
447
  "$ref": "#/$defs/PathOrNull"
456
448
  },
457
- "group": {
458
- "type": "array",
459
- "minItems": 2,
460
- "items": {
461
- "$ref": "https://schemas.plurnk.xyz/v0/ResourceSelection.json"
462
- },
463
- "description": "The heading's target group ({§target-group}): every `(path)` slot with the scope and metadata that follow it, in authored order, the first repeating `target`, `lineMarker` and `metadata`. Present only when the heading carries more than one slot; absent, as in every statement stored before groups existed, the operation has its one target."
464
- },
465
449
  "lineMarker": {
466
450
  "$ref": "#/$defs/TextLineMarkerOrNull",
467
451
  "description": "Scope of the kill: lines of a log body or of an entry ({§kill-scope}); null kills the whole target."
@@ -31,7 +31,7 @@
31
31
  "instance": {
32
32
  "type": "string",
33
33
  "pattern": "^[a-zA-Z][a-zA-Z0-9+.-]*:",
34
- "description": "Absolute URI identifying the failed durable operation, normally its log URI. Core adds it at persistence when a plugin cannot know the durable coordinate."
34
+ "description": "Absolute URI identifying the failed durable operation, normally its log URI. Core adds it at persistence when an extension cannot know the durable coordinate."
35
35
  },
36
36
  "stage": {
37
37
  "type": "string",
@@ -2,22 +2,21 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$id": "https://schemas.plurnk.xyz/v0/ProposalDisposition.json",
4
4
  "title": "ProposalDisposition",
5
- "description": "The one core-owned settlement decision for a pending proposal. Client-owned proposals require an external resolution; loop-owned proposals carry the exact automatic resolution core applies.",
5
+ "description": "The one core-owned settlement decision. Review waits for the worker's owner; accept and reject carry the automatic resolution core applies.",
6
6
  "oneOf": [
7
7
  {
8
8
  "type": "object",
9
- "required": ["owner"],
9
+ "required": ["decision"],
10
10
  "additionalProperties": false,
11
11
  "properties": {
12
- "owner": { "const": "client" }
12
+ "decision": { "const": "review" }
13
13
  }
14
14
  },
15
15
  {
16
16
  "type": "object",
17
- "required": ["owner", "decision"],
17
+ "required": ["decision"],
18
18
  "additionalProperties": false,
19
19
  "properties": {
20
- "owner": { "const": "loop" },
21
20
  "decision": { "enum": ["accept", "reject"] },
22
21
  "outcome": { "type": "string", "minLength": 1 }
23
22
  }
@@ -0,0 +1,7 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.plurnk.xyz/v0/ProposalPolicy.json",
4
+ "title": "ProposalPolicy",
5
+ "description": "Server disposition for proposals. Review requires the worker's owner; accept and reject settle within core.",
6
+ "enum": ["review", "accept", "reject"]
7
+ }
@@ -13,7 +13,7 @@
13
13
  "target",
14
14
  "body",
15
15
  "attrs",
16
- "policy",
16
+ "owner",
17
17
  "disposition"
18
18
  ],
19
19
  "additionalProperties": false,
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "body": { "type": "string" },
44
44
  "attrs": { "type": "object", "additionalProperties": true },
45
- "policy": { "$ref": "https://schemas.plurnk.xyz/v0/LoopPolicy.json" },
45
+ "owner": { "type": "string", "minLength": 1 },
46
46
  "disposition": { "$ref": "https://schemas.plurnk.xyz/v0/ProposalDisposition.json" }
47
47
  }
48
48
  }
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://schemas.plurnk.xyz/v0/WorkerOwner.json",
4
+ "title": "WorkerOwner",
5
+ "description": "A workspace-scoped approval owner and the client tools it implements. Capabilities persist while disconnected; a message never changes ownership.",
6
+ "type": "object",
7
+ "required": ["address", "tools"],
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "address": { "type": "string", "minLength": 1 },
11
+ "tools": {
12
+ "type": "array",
13
+ "uniqueItems": true,
14
+ "items": { "type": "string", "minLength": 1 }
15
+ }
16
+ }
17
+ }