@plurnk/plurnk-contracts 6.0.0 → 7.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 +5 -5
- package/README.md +4 -4
- package/SPEC.md +158 -2054
- package/dist/schema/{AguiDiscovery.json → AcpDiscovery.json} +4 -4
- package/dist/schema/ApplicationOperationEvent.json +2 -2
- package/dist/schema/CapabilityDescriptor.json +3 -3
- package/dist/schema/CapabilityPolicy.json +5 -1
- package/dist/schema/CapabilitySelector.json +3 -3
- package/dist/schema/ClientInteractionProjection.json +2 -2
- package/dist/schema/ClientInteractionRequest.json +26 -10
- package/dist/schema/ClientInteractionResolution.json +14 -6
- package/dist/schema/FunctionalityDefinitionState.json +3 -3
- package/dist/schema/FunctionalityDiscoverQuery.json +2 -1
- package/dist/schema/FunctionalityMutationResult.json +1 -1
- package/dist/schema/MatcherBody.json +2 -2
- package/dist/schema/McpServerDefinition.json +35 -9
- package/dist/schema/ModelCatalogPage.json +1 -1
- package/dist/schema/ModelRoute.json +2 -2
- package/dist/schema/Notice.json +1 -1
- package/dist/schema/PlurnkStatement.json +8 -8
- package/dist/schema/ProposalDisposition.json +1 -1
- package/dist/schema/ProposalPolicy.json +1 -1
- package/dist/schema/ProposalProjection.json +57 -6
- package/dist/schema/ResourceSelection.json +1 -1
- package/dist/schema/RollbackReport.json +1 -1
- package/dist/schema/{WorkerOwner.json → SessionOwner.json} +2 -2
- package/dist/schema/SkillDefinition.json +8 -7
- package/dist/src/Application.d.ts +97 -66
- package/dist/src/Application.d.ts.map +1 -1
- package/dist/src/Application.js +198 -1
- package/dist/src/Application.js.map +1 -1
- package/dist/src/ApplicationNotifications.d.ts +52 -0
- package/dist/src/ApplicationNotifications.d.ts.map +1 -0
- package/dist/src/ApplicationNotifications.js +2 -0
- package/dist/src/ApplicationNotifications.js.map +1 -0
- package/dist/src/ApplicationPort.d.ts +84 -75
- package/dist/src/ApplicationPort.d.ts.map +1 -1
- package/dist/src/ApplicationStream.d.ts +4 -4
- package/dist/src/ApplicationStream.d.ts.map +1 -1
- package/dist/src/CapabilityAdmission.d.ts +2 -0
- package/dist/src/CapabilityAdmission.d.ts.map +1 -1
- package/dist/src/CapabilityAdmission.js +14 -2
- package/dist/src/CapabilityAdmission.js.map +1 -1
- package/dist/src/Excerpt.js +1 -1
- package/dist/src/Excerpt.js.map +1 -1
- package/dist/src/Functionality.d.ts +3 -6
- package/dist/src/Functionality.d.ts.map +1 -1
- package/dist/src/HttpOrigin.d.ts +3 -3
- package/dist/src/HttpOrigin.js +1 -1
- package/dist/src/JsonDocument.js +1 -1
- package/dist/src/JsonDocument.js.map +1 -1
- package/dist/src/JsonResult.js +1 -1
- package/dist/src/JsonResult.js.map +1 -1
- package/dist/src/LogRow.d.ts +31 -0
- package/dist/src/LogRow.d.ts.map +1 -0
- package/dist/src/LogRow.js +46 -0
- package/dist/src/LogRow.js.map +1 -0
- package/dist/src/LoopLifecycle.d.ts +1 -1
- package/dist/src/LoopLifecycle.d.ts.map +1 -1
- package/dist/src/LoopLifecycle.js +2 -2
- package/dist/src/LoopLifecycle.js.map +1 -1
- package/dist/src/MessageResource.d.ts +4 -4
- package/dist/src/MessageResource.d.ts.map +1 -1
- package/dist/src/PathSyntax.js +2 -2
- package/dist/src/PathSyntax.js.map +1 -1
- package/dist/src/PlurnkParseError.js +1 -1
- package/dist/src/PlurnkParseError.js.map +1 -1
- package/dist/src/Problems.js +1 -1
- package/dist/src/Problems.js.map +1 -1
- package/dist/src/TurnDisposition.js +1 -1
- package/dist/src/TurnDisposition.js.map +1 -1
- package/dist/src/Validator.d.ts +5 -108
- package/dist/src/Validator.d.ts.map +1 -1
- package/dist/src/Validator.js +97 -518
- package/dist/src/Validator.js.map +1 -1
- package/dist/src/guards.js +1 -1
- package/dist/src/guards.js.map +1 -1
- package/dist/src/index.d.ts +6 -5
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +7 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/types.d.ts +1 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.generated.d.ts +91 -187
- package/dist/src/types.generated.d.ts.map +1 -1
- package/dist/src/types.js +7 -7
- package/dist/src/types.js.map +1 -1
- package/package.json +5 -9
- package/plurnk.md +22 -22
- package/dist/conformance/agui-v1.json +0 -716
- package/dist/schema/A2aAgentDefinition.json +0 -76
- package/dist/schema/AguiClientConformance.json +0 -74
- package/dist/schema/AguiConformanceKit.json +0 -111
- package/dist/src/AguiConformance.d.ts +0 -11
- package/dist/src/AguiConformance.d.ts.map +0 -1
- package/dist/src/AguiConformance.js +0 -16
- package/dist/src/AguiConformance.js.map +0 -1
package/SPEC.md
CHANGED
|
@@ -1,129 +1,22 @@
|
|
|
1
1
|
# PLURNK Contracts Specification
|
|
2
2
|
|
|
3
|
-
## 1. Overview
|
|
3
|
+
## §contract-representations 1. Overview and schema representations
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
| Tag | Promise |
|
|
7
|
-
| --- | --- |
|
|
8
|
-
| {§contract-representations} | JSON Schema is authoritative for shared data shapes. |
|
|
9
|
-
| {§agui-discovery-contract} | `AguiDiscovery` is the complete installed AG-UI+ surface at one instant. |
|
|
10
|
-
| {§agui-action-schema-enforcement} | The JSON Schema values in {§agui-discovery-contract} are executable boundary contracts, not prose or hints. |
|
|
11
|
-
| {§agui-client-conformance} | `AguiClientConformance` is a language-neutral JSON document accounting for every action and notification in… |
|
|
12
|
-
| {§agui-conformance-kit} | `AguiConformanceKit` is the one versioned, language-neutral corpus of raw SSE boundary specimens and AG-UI… |
|
|
13
|
-
| {§json-result-rendering} | `renderJsonResult` renders compact aggregate operation rows. |
|
|
14
|
-
| {§json-document-presentation} | Generated JSON documents use two-space indentation. |
|
|
15
|
-
<!-- /map -->
|
|
16
|
-
|
|
17
|
-
This package is the single authority for PLURNK's language, schemas, generated types and
|
|
18
|
-
runtime-neutral wire envelopes; `@plurnk/plurnk-parser` implements the language it specifies
|
|
19
|
-
({§parser-consumers}). Its package root is the single code API for those contracts
|
|
20
|
-
({§root-value-api}).
|
|
21
|
-
|
|
22
|
-
| Surface | Canonical export or artifact |
|
|
23
|
-
| ------------------------------------------------------------------------------- | --------------------------------------------------- |
|
|
24
|
-
| AST, validators, Problems, results, Notices, text regions and extents | `@plurnk/plurnk-contracts` |
|
|
25
|
-
| Capability admission and approval ownership | `CapabilityPolicy`, `WorkerOwner`, `ProposalPolicy`, `PROPOSAL_POLICIES` |
|
|
26
|
-
| Durable effort | `Effort`, `EFFORTS` |
|
|
27
|
-
| Model route and catalog discovery | `ModelRoute`, `ModelCatalogQuery`, `ModelCatalogPage`, `ModelReadiness` |
|
|
28
|
-
| Stopped-world client contract | `ProposalDisposition`, `ProposalProjection` |
|
|
29
|
-
| Client-owned interaction contract | `ClientInteractionRequest`, `ClientInteractionProjection`, `ClientInteractionResolution` |
|
|
30
|
-
| Client capability presentation | `ClientDisplayCapabilities` |
|
|
31
|
-
| Exterior adapter application calls | `ApplicationPort`, from `@plurnk/plurnk-contracts/port` ({§runtime-neutral-root}) |
|
|
32
|
-
| Workspace MCP configuration | `McpServerDefinition`, `McpOAuth` |
|
|
33
|
-
| Worker Agent Skills definition | `SkillDefinition` |
|
|
34
|
-
| Worker outbound A2A agent definition | `A2aAgentDefinition` |
|
|
35
|
-
| Worker Functionality lifecycle projections (family-neutral) | `FunctionalityProvenance`, `FunctionalityCandidate`, `FunctionalityDiscoverQuery`, `FunctionalityDiscoverResult`, `FunctionalityDefinitionState`, `FunctionalityListResult`, `FunctionalityMutationResult` |
|
|
36
|
-
| AG-UI discovery, client accounting, and shared conformance specimens | `AguiDiscovery`, `AguiClientConformance`, `AguiConformanceKit` |
|
|
37
|
-
| JSON Schemas | `@plurnk/plurnk-contracts/schema/*.json` |
|
|
38
|
-
| Generated JSON result rendering | `renderJsonResult` |
|
|
39
|
-
| Model language reference | `plurnk.md` in the package |
|
|
40
|
-
|
|
41
|
-
§contract-representations JSON Schema is authoritative for shared data shapes. TypeScript types are
|
|
42
|
-
generated from the schemas; ANTLR is authoritative for accepted model-language
|
|
43
|
-
syntax. An operator's own GBNF is carried verbatim to a llama-server route by
|
|
44
|
-
the providers package.
|
|
45
|
-
|
|
46
|
-
§agui-discovery-contract `AguiDiscovery` is the complete installed AG-UI+
|
|
47
|
-
surface at one instant. `schemaVersion` identifies its discovery shape;
|
|
48
|
-
`actions` maps each unique public name to exactly one `scope`, `inputSchema`,
|
|
49
|
-
and `outputSchema`; `notifications` maps each unique event-family name to one
|
|
50
|
-
`payloadSchema`; and `display` carries {§client-display-capabilities} without
|
|
51
|
-
another presentation mechanism. The AG-UI owner supplies the built-in registry;
|
|
52
|
-
an extension contributes the same schema-bearing action descriptor through its
|
|
53
|
-
core module registration rather than creating a second action type.
|
|
54
|
-
|
|
55
|
-
§agui-action-schema-enforcement The JSON Schema values in
|
|
56
|
-
{§agui-discovery-contract} are executable boundary contracts, not prose or
|
|
57
|
-
hints. The AG-UI boundary rejects an action input before dispatch when it does
|
|
58
|
-
not satisfy the advertised `inputSchema`, rejects an owner's successful output
|
|
59
|
-
when it does not satisfy `outputSchema`, and validates a known notification
|
|
60
|
-
before projecting it to AG-UI. Schemas are discovery values owned by their
|
|
61
|
-
registrants; validation must not annotate or otherwise mutate them.
|
|
62
|
-
Input Problems retain the structured validation `issues` and name the failing
|
|
63
|
-
instance locations and constraints in `detail`. Parent aggregate errors are
|
|
64
|
-
omitted from that prose when their specific child errors are available; input
|
|
65
|
-
objects are never echoed wholesale or interpreted as intent.
|
|
66
|
-
|
|
67
|
-
§agui-client-conformance `AguiClientConformance` is a language-neutral JSON
|
|
68
|
-
document accounting for every action and notification in one
|
|
69
|
-
{§agui-discovery-contract}. Each name is classified as `native` (dedicated
|
|
70
|
-
client behavior), `generic` (lossless protocol support without dedicated UI),
|
|
71
|
-
or `unsupported` with an explicit reason, and cites nonempty verification
|
|
72
|
-
evidence. Each disposition declares the exact verification dimensions its
|
|
73
|
-
evidence covers; native behavior includes admission and presentation, every
|
|
74
|
-
action includes projection plus success and failure, and every notification
|
|
75
|
-
includes framing plus projection. Validation requires exact action and
|
|
76
|
-
notification key equality with the installed discovery surface; adding or
|
|
77
|
-
removing a public capability therefore breaks every stale client matrix
|
|
78
|
-
visibly. The contracts-owned report procedure resolves every cited evidence
|
|
79
|
-
path and emits one record per member with its posture and verified dimensions;
|
|
80
|
-
a stale or fictional citation fails the report.
|
|
81
|
-
|
|
82
|
-
§agui-conformance-kit `AguiConformanceKit` is the one versioned,
|
|
83
|
-
language-neutral corpus of raw SSE boundary specimens and AG-UI lifecycle
|
|
84
|
-
sequences used by every client transport. Its JSON resource is test input, not
|
|
85
|
-
a third protocol implementation: each client feeds the same chunks and events
|
|
86
|
-
through its production parser and projection seam, then verifies the declared
|
|
87
|
-
outcome. Specimen names are unique within their transport or lifecycle family.
|
|
88
|
-
|
|
89
|
-
§json-result-rendering `renderJsonResult` renders compact aggregate operation
|
|
90
|
-
rows. A top-level array remains one valid,
|
|
91
|
-
compact JSON value but places each item on its own physical line by adding only
|
|
92
|
-
item-boundary newlines; an empty or single-item array and every non-array value
|
|
93
|
-
remain one line. It never rewrites arbitrary stored JSON, whose original lines
|
|
94
|
-
remain source coordinates.
|
|
95
|
-
|
|
96
|
-
§json-document-presentation Generated JSON documents use two-space indentation.
|
|
97
|
-
Normalized remote JSON text uses `formatJsonDocument`: whitespace-only formatting
|
|
98
|
-
of a complete, strict JSON document, preserving key order, duplicate keys, number
|
|
99
|
-
lexemes, and string escapes. Invalid or incomplete input is declined, not repaired.
|
|
100
|
-
Apply formatting at the representation owner before storage, indexing, scoping,
|
|
101
|
-
and previews; never reformat literal resources, JSONL framing, or wire/evidence
|
|
102
|
-
serialization. Compact aggregate rows ({§json-result-rendering}) and packet
|
|
103
|
-
metadata retain their deliberate layouts.
|
|
5
|
+
`@plurnk/plurnk-contracts` is the single authority for PLURNK's model-facing language specification, JSON Schemas (`schema/*.json`), generated TypeScript types, and runtime-neutral wire envelopes. `@plurnk/plurnk-parser` implements the language it specifies ({§parser-build}).
|
|
104
6
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
<!-- map -->
|
|
108
|
-
| Tag | Promise |
|
|
7
|
+
| Surface | Canonical export or artifact |
|
|
109
8
|
| --- | --- |
|
|
110
|
-
|
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
|
|
|
115
|
-
|
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
|
|
|
119
|
-
|
|
|
120
|
-
|
|
|
121
|
-
| {§provider-request-evidence} | The physical-request settlement may also carry JSON-serializable, provider-owned response/failure evidence. |
|
|
122
|
-
| {§provider-accounting} | `ProviderAccounting.requests` is the source evidence. |
|
|
123
|
-
<!-- /map -->
|
|
124
|
-
|
|
125
|
-
One contract, deliberately different projections (ARCHITECTURE.md). Each layer
|
|
126
|
-
below owns what it alone can decide.
|
|
9
|
+
| AST, validators, Problems, results, Notices, text regions and extents | `@plurnk/plurnk-contracts` |
|
|
10
|
+
| Capability admission and approval ownership | `CapabilityPolicy`, `SessionOwner`, `ProposalPolicy`, `PROPOSAL_POLICIES` |
|
|
11
|
+
| Durable effort and model catalog discovery | `Effort`, `EFFORTS`, `ModelRoute`, `ModelCatalogQuery`, `ModelCatalogPage`, `ModelReadiness` |
|
|
12
|
+
| Stopped-world proposals and client interactions | `ProposalDisposition`, `ProposalProjection`, `ClientInteractionRequest`, `ClientInteractionProjection`, `ClientInteractionResolution` |
|
|
13
|
+
| Client capability presentation | `ClientDisplayCapabilities` |
|
|
14
|
+
| Exterior adapter application calls | `ApplicationPort`, from `@plurnk/plurnk-contracts/port` |
|
|
15
|
+
| Workspace MCP and Agent Skills definitions | `McpServerDefinition`, `McpOAuth`, `SkillDefinition` |
|
|
16
|
+
| Session Functionality lifecycle projections | `FunctionalityProvenance`, `FunctionalityCandidate`, `FunctionalityDiscoverQuery`, `FunctionalityDiscoverResult`, `FunctionalityDefinitionState`, `FunctionalityListResult`, `FunctionalityMutationResult` |
|
|
17
|
+
| ACP discovery manifest | `AcpDiscovery` |
|
|
18
|
+
| JSON Schemas and JSON rendering | `@plurnk/plurnk-contracts/schema/*.json`, `renderJsonResult`, `formatJsonDocument` |
|
|
19
|
+
| Model language reference | `plurnk.md` in the package |
|
|
127
20
|
|
|
128
21
|
```mermaid
|
|
129
22
|
flowchart LR
|
|
@@ -139,2004 +32,215 @@ flowchart LR
|
|
|
139
32
|
ast --> runtime
|
|
140
33
|
```
|
|
141
34
|
|
|
142
|
-
| Layer
|
|
143
|
-
|
|
144
|
-
| Stable current law
|
|
145
|
-
| Canonical model teaching | `plurnk.md`
|
|
146
|
-
| Accepted syntax
|
|
147
|
-
| Typed admission
|
|
148
|
-
| Shared wire data
|
|
149
|
-
| Stateful behavior
|
|
150
|
-
|
|
151
|
-
ANTLR owns statement structure, fence matching, slot multiplicity, accepted
|
|
152
|
-
slot permutations, scope-number syntax, and interstatement text recognition.
|
|
153
|
-
AstBuilder owns URL decomposition and deterministic matcher validation through
|
|
154
|
-
WHATWG `URL`, ECMAScript `RegExp`, XPath 1.0, and RFC 9535 JSONPath parsers.
|
|
35
|
+
| Layer | Owner or artifact | Contract |
|
|
36
|
+
| --- | --- | --- |
|
|
37
|
+
| Stable current law | `SPEC.md` | Owns invariants and boundaries; forge issues retain history |
|
|
38
|
+
| Canonical model teaching | `plurnk.md` | Teaches the lean spelling and operational model the model should emit |
|
|
39
|
+
| Accepted syntax | `plurnkLexer.g4`, `plurnkParser.g4` | Recognizes document tiers, operation fences, slot shape, and section boundaries |
|
|
40
|
+
| Typed admission | `AstBuilder` | Produces JSON-serializable unions and validates deterministic body/path syntax |
|
|
41
|
+
| Shared wire data | `schema/*.json` | Defines runtime-neutral data shapes projected into generated TypeScript |
|
|
42
|
+
| Stateful behavior | consuming runtime | Resolves addresses, permissions, selection arithmetic, effects, and lifecycle |
|
|
155
43
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
effects, and executor behavior.
|
|
44
|
+
- **Authoritative representations**: JSON Schema is authoritative for shared wire data shapes; TypeScript types are generated from `schema/*.json`; ANTLR grammars in `@plurnk/plurnk-parser` are authoritative for accepted model-language syntax.
|
|
45
|
+
- **ACP discovery and schema enforcement**: `AcpDiscovery` enumerates the installed ACP v2 actions (`scope`, `inputSchema`, `outputSchema`), notifications (`payloadSchema`), and `display` capabilities. Boundary validators enforce these schemas without mutating them and report structured validation `issues` in RFC 9457 Problem `detail`.
|
|
46
|
+
- **JSON result and document presentation**: `renderJsonResult` formats compact aggregate operation rows (placing top-level array items on individual lines without rewriting stored JSON coordinates). `formatJsonDocument` performs two-space whitespace-only formatting of complete strict JSON documents before representation storage.
|
|
160
47
|
|
|
161
|
-
### §
|
|
48
|
+
### §capability-policy-cascade Capability admission, proposals, and model catalog
|
|
162
49
|
|
|
163
|
-
|
|
50
|
+
```mermaid
|
|
51
|
+
flowchart LR
|
|
52
|
+
S["Service CapabilityPolicy"] & W["Workspace CapabilityPolicy"] --> E["Effective intersection (deny > only > allow)"]
|
|
53
|
+
E --> D{"Admitted?"}
|
|
54
|
+
D -->|no| R["403 capability-denied"]
|
|
55
|
+
D -->|yes, allow match| A["Auto-accepted execution"]
|
|
56
|
+
D -->|yes, gated| P["ProposalDisposition (review | accept | reject)"]
|
|
57
|
+
```
|
|
164
58
|
|
|
165
|
-
| Contract
|
|
166
|
-
|
|
|
167
|
-
| `CapabilityDescriptor`
|
|
168
|
-
| `CapabilityPolicy`
|
|
169
|
-
| `CapabilityProjection`
|
|
170
|
-
| `
|
|
171
|
-
| `ProposalPolicy` | `review`, `accept`, or `reject` | Read the server's disposition knob at the proposal boundary |
|
|
59
|
+
| Contract | Shape invariant | Runtime responsibility |
|
|
60
|
+
| --- | --- | --- |
|
|
61
|
+
| `CapabilityDescriptor` | One routed operation demand with its operation, access class (`read \| write \| exec \| network \| session \| interact`), resource/runtime/tool coordinates, and declared traits | Derive every demand before dispatch |
|
|
62
|
+
| `CapabilityPolicy` | Exact `allow`/`only`/`deny` selectors; omitted `only` is unrestricted, present empty `only` denies all, and `allow` pre-approves admitted proposals | Intersect service and workspace policies |
|
|
63
|
+
| `CapabilityProjection` | Durable `service`, client-mutable `workspace`, and normalized `effective` policies | Expose effective authority without storing session/loop policy copies |
|
|
64
|
+
| `ProposalPolicy` | `review`, `accept`, or `reject` (`PROPOSAL_POLICIES`) | Read the server's disposition knob at the proposal boundary |
|
|
172
65
|
| `ProposalDisposition` | Review, or automatic accept/reject with optional outcome | Select once for the pending gate |
|
|
173
|
-
| `ProposalProjection` | Identity, review target, body/attrs, owner, stale signal, disposition | One validated projection for live delivery and reconnect |
|
|
174
|
-
| `ProviderUsage` | Conventional input/output totals with cache and reasoning details | Preserve observed quantities without replacing absence with zero |
|
|
175
|
-
| `ProviderCost` | Exact charged, estimated, or unknown monetary evidence | Normalize one monetary disposition for each physical provider request |
|
|
176
|
-
| `ProviderRequestAccounting` | Usage and cost evidence for one physical provider request | Preserve request order across retries, failover, success, and failure |
|
|
177
|
-
| `ProviderAccounting` | Ordered requests plus deterministic usage and exact-USD projections | Derive loop, protocol, telemetry, and client reporting without a second authority |
|
|
178
|
-
|
|
179
|
-
§capability-policy-matching A selector is an exact conjunction: every field it
|
|
180
|
-
declares must equal the descriptor, while every selected `trait` must occur in
|
|
181
|
-
the descriptor's trait set. `deny` wins within a layer. When `only` is present, at
|
|
182
|
-
least one selector must match. An empty policy admits everything and an empty
|
|
183
|
-
`only` list admits nothing.
|
|
66
|
+
| `ProposalProjection` | Identity, review target, body/attrs, owner, capability selector, ACP v2 subject, stale signal, disposition | One validated projection for live delivery and reconnect |
|
|
184
67
|
|
|
185
|
-
|
|
186
|
-
order-independent: a descriptor is admitted only when every layer admits it.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
descriptors govern routed external authority, not every grammar statement:
|
|
190
|
-
log/program control such as log KILL, the native dispositions, and
|
|
191
|
-
targetless SEND creates no capability demand. A known interactive runtime is represented by
|
|
192
|
-
access class `interact`; scheme and runtime manifests contribute traits rather
|
|
193
|
-
than hidden policy behavior.
|
|
68
|
+
- **Exact selector conjunction**: A capability selector matches a `CapabilityDescriptor` when every declared field equals the descriptor and every declared `trait` occurs in the descriptor's trait set. Within a policy layer, `deny` wins over `only` and `allow`; when `only` is present, at least one selector must match. An empty policy admits everything; an empty `only` list admits nothing.
|
|
69
|
+
- **Subtractive cascade**: Capability admission is purely subtractive and order-independent: a descriptor is admitted only when every active layer (`service` and `workspace`) admits it. Workspace policy cannot restore authority denied by the service. A demand is pre-approved across layers only when it is admitted across every layer and matches at least one layer's `allow` selector. Log/program control (`KILL (log:///...)`, native dispositions `WAIT`/`NOTE`, and targetless `SEND`) creates no capability demand.
|
|
70
|
+
- **Proposals and session ownership**: `ProposalPolicy` (`review | accept | reject`) and `SessionOwner` (`address`, `clientTools`, `interactive`) govern stopped-world approval after capability admission succeeds.
|
|
71
|
+
- **Effort and model catalog wire**: `Effort` is `off | adaptive | low | medium | high | xhigh | max`. `ModelRoute`, `ModelCatalogPage`, and `ModelReadiness` expose provider/model identity, capabilities (including supported `efforts`), physical limits, and alternative required environment-variable name sets (names only, never values, credentials, or tuning internals).
|
|
194
72
|
|
|
195
|
-
§
|
|
196
|
-
workspace layer and their normalized intersection: `service`, `workspace`, and
|
|
197
|
-
`effective`. Only `workspace` is client-mutable. Workers and loops store no
|
|
198
|
-
capability policy and inherit no bound; every actor uses the same live workspace
|
|
199
|
-
policy. Core derives one further ring per asking worker from its owner, and it only
|
|
200
|
-
removes interaction runtimes ({§owner-interaction-ring}). A client never derives
|
|
201
|
-
effective authority from the mutable layer alone.
|
|
202
|
-
|
|
203
|
-
§proposal-policy `ProposalPolicy` names one server disposition; `PROPOSAL_POLICIES`
|
|
204
|
-
is its schema-owned vocabulary. `WorkerOwner` carries an address, the client-tool
|
|
205
|
-
names that recipient implements, and `interactive`: whether a person attends it. Neither
|
|
206
|
-
contains resource permissions or
|
|
207
|
-
message-carried authority. Core owns persistence, inheritance and settlement
|
|
208
|
-
({§worker-ownership}, {§proposal-disposition}); capability admission precedes approval.
|
|
209
|
-
|
|
210
|
-
§effort-wire `Effort` is exactly `off | adaptive | low |
|
|
211
|
-
medium | high | xhigh | max`. The schema owns this shared wire vocabulary. Providers own the
|
|
212
|
-
supported subset and native projection for a selected route; core owns the
|
|
213
|
-
durable worker value.
|
|
214
|
-
|
|
215
|
-
§model-catalog-wire `ModelRoute` is one exact client-visible provider/model
|
|
216
|
-
identity with optional alias provenance. Provider credentials, endpoints, and
|
|
217
|
-
tuning never enter this wire shape. Catalog discovery uses a
|
|
218
|
-
closed bounded query and page: entries carry exact selectors, display facts,
|
|
219
|
-
physical limits, capabilities, and local `ModelReadiness`. A readiness cause
|
|
220
|
-
contains alternative environment-variable sets—every name within a set is
|
|
221
|
-
required and any set may satisfy the cause. It carries names only, never values,
|
|
222
|
-
and asserts neither credential validity nor endpoint reachability.
|
|
223
|
-
`capabilities.efforts` lists the route's admitted members of
|
|
224
|
-
{§effort-wire}, including supported activation policies; clients do
|
|
225
|
-
not infer fixed efforts from the `reasoning` capability bit. It is not a
|
|
226
|
-
worker's model/spawn intersection or an alias-specific tuning projection.
|
|
227
|
-
|
|
228
|
-
### §client-interaction-wire Client-owned interaction wire
|
|
229
|
-
|
|
230
|
-
The closed client-interaction schemas describe one operation asking its client
|
|
231
|
-
for structured input without transferring private protocol continuation state.
|
|
73
|
+
### §provider-request-accounting Client interactions and provider request accounting
|
|
232
74
|
|
|
233
75
|
| Contract | Shape invariant |
|
|
234
|
-
|
|
235
|
-
| `ClientInteractionRequest` | Non-empty `toolName`, object `
|
|
236
|
-
| `ClientInteractionProjection` | Positive interaction,
|
|
237
|
-
| `ClientInteractionResolution` | Exactly `{
|
|
238
|
-
|
|
239
|
-
Opaque upstream request identities, retry state, credentials, and callbacks are
|
|
240
|
-
not members of these schemas. The operation owner retains and interprets those
|
|
241
|
-
facts; core and client interfaces carry only this standard interaction.
|
|
242
|
-
|
|
243
|
-
§provider-usage `ProviderUsage` records only known non-negative safe-integer
|
|
244
|
-
quantities. `inputTokens` includes every input category; cache reads and cache
|
|
245
|
-
writes are details within it. `outputTokens` includes reasoning; text and
|
|
246
|
-
reasoning are details within it. Within one physical request, `totalTokens`
|
|
247
|
-
equals input plus output whenever all three are present, a detail is no greater
|
|
248
|
-
than its containing total, and a complete detail partition sums to that total.
|
|
249
|
-
An omitted field is unknown; an explicit zero is provider evidence or an exact
|
|
250
|
-
derivation from complete known components. Consumers never estimate a token
|
|
251
|
-
category from text length.
|
|
252
|
-
|
|
253
|
-
§provider-cost `ProviderCost` represents one physical provider request's
|
|
254
|
-
monetary disposition. `charged` preserves a provider-reported canonical decimal
|
|
255
|
-
amount and currency, with an optional decimal USD equivalent for a non-USD
|
|
256
|
-
charge. `estimated` preserves a calculated decimal amount and currency.
|
|
257
|
-
`unknown` requires a reason. Zero is an ordinary exact amount under `charged`
|
|
258
|
-
or `estimated`; there is no separate free mechanism. Decimal strings preserve
|
|
259
|
-
evidence without binary floating-point rewriting. Unknown is not zero.
|
|
260
|
-
|
|
261
|
-
§provider-request-accounting `ProviderRequestAccounting` is the indivisible
|
|
262
|
-
accounting fact for one issued physical request. Its provider, model, outcome,
|
|
263
|
-
optional protocol status, optional {§provider-usage}, and required
|
|
264
|
-
{§provider-cost} travel together. Ordered request records preserve retries and
|
|
265
|
-
capacity failover; a later response never replaces an earlier request.
|
|
266
|
-
|
|
267
|
-
§provider-request-evidence The physical-request settlement may also carry
|
|
268
|
-
JSON-serializable, provider-owned response/failure evidence. This is forensic
|
|
269
|
-
data, separate from accounting and executable output. Consumers persist it
|
|
270
|
-
with the same request, including failed requests before a successful retry;
|
|
271
|
-
they never substitute it for an admitted response. Missing historical evidence
|
|
272
|
-
remains absent. Heavy evidence is not copied into accounting or client events.
|
|
273
|
-
|
|
274
|
-
§provider-accounting `ProviderAccounting.requests` is the source evidence.
|
|
275
|
-
The projections are derived, not independent inputs:
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `ClientInteractionRequest` | Non-empty `toolName`, `mode: "form"` with non-empty `message` and object `requestedSchema`, or `mode: "url"` with non-empty `message`, non-empty `elicitationId`, and URI `url` |
|
|
78
|
+
| `ClientInteractionProjection` | Positive interaction, session, loop, and turn identities plus the exact request; workspace scope remains the containing transport envelope |
|
|
79
|
+
| `ClientInteractionResolution` | Exactly `{ action: "accept", content? }`, `{ action: "decline" }`, or `{ action: "cancel" }` |
|
|
276
80
|
|
|
277
81
|
| Projection | Complete total | Known subtotal |
|
|
278
82
|
| --- | --- | --- |
|
|
279
|
-
| Tokens | Each `usage` field is present only if every request reports that quantity
|
|
280
|
-
|
|
|
83
|
+
| Tokens (`ProviderUsage`) | Each `usage` field (`inputTokens`, `outputTokens`, `totalTokens`, cache and reasoning details) is present only if every request reports that quantity; `totalTokens === inputTokens + outputTokens` when all three are present | `knownUsage` sums each reported non-negative safe-integer field, omitting wholly unknown quantities without substituting zero for unknown |
|
|
84
|
+
| Monetary (`ProviderCost`) | `costUsd` is `null` if any request lacks USD-expressible cost (`charged` or `estimated` decimal string; `unknown` requires a `reason`) | `knownCostUsd` sums expressible USD costs as an exact decimal string, or is `null` when none are known |
|
|
281
85
|
|
|
282
|
-
|
|
283
|
-
Unknown is never zero. Human presentations identify a subtotal as incomplete;
|
|
284
|
-
comparative totals, ratios, and cost-per-success use only complete evidence.
|
|
285
|
-
Consumers do not reconstruct rates or currencies from these projections.
|
|
86
|
+
- **Indivisible request accounting and evidence**: `ProviderRequestAccounting` binds one physical request's provider, model, outcome, optional protocol status, `ProviderUsage`, and `ProviderCost` in issue order across retries and capacity failover; a later response never replaces an earlier request. JSON-serializable forensic response/failure payloads travel alongside as separate evidence without entering client events.
|
|
286
87
|
|
|
287
|
-
|
|
288
|
-
under {§whitespace-contract}. It recovers at a
|
|
289
|
-
trustworthy statement boundary when possible and sets `unparsedTail` when a
|
|
290
|
-
boundary-destroying failure makes later input undefined. Operation status codes
|
|
291
|
-
and parse diagnostics are separate contracts.
|
|
88
|
+
---
|
|
292
89
|
|
|
293
|
-
## §
|
|
294
|
-
|
|
295
|
-
<!-- map -->
|
|
296
|
-
| Tag | Promise |
|
|
297
|
-
| --- | --- |
|
|
298
|
-
| {§section-boundary} | Every statement is one backtick block. |
|
|
299
|
-
| {§fence-closer} | A block opened with N backticks closes at a line made of at least N backticks and nothing else but horizontal… |
|
|
300
|
-
| {§balanced-fences} | A complete nested interpretation takes precedence over missing-closer recovery. |
|
|
301
|
-
| {§operation-fences} | Accept three or more backticks; teach and render three. |
|
|
302
|
-
| {§naked-operation} | A native name with only an optional aside opens without a fence. |
|
|
303
|
-
| {§fence-heading-in-body} | Outside a complete nested block ({§balanced-fences}), a fence line of three or more backticks carrying a… |
|
|
304
|
-
| {§closer-fallback} | A block that ends at a heading or at the end of the input has no closer of its own; its body is the whole… |
|
|
305
|
-
| {§fence-boundary} | Every fence line inside a body takes one of these meanings, chosen for the whole input at once by… |
|
|
306
|
-
| {§fence-pairing} | Fence pairing |
|
|
307
|
-
| {§pairing-need} | Why a pass ahead of the grammar. |
|
|
308
|
-
| {§pairing-algorithm} | Algorithm. |
|
|
309
|
-
| {§pairing-objective} | Objective. |
|
|
310
|
-
| {§unclosed-mutation-yields} | A mutation body never swallows a native operation. |
|
|
311
|
-
| {§prose-code-blocks} | A message or a prompt shows code. |
|
|
312
|
-
| {§message-run-on} | A message runs to the end of the turn. |
|
|
313
|
-
| {§pairing-witness} | Witnesses. |
|
|
314
|
-
| {§indented-fences} | CommonMark's indentation, everywhere. |
|
|
315
|
-
| {§inline-chain} | A closer on a heading line, or on a body's closing line, may be followed on that same line by the next… |
|
|
316
|
-
| {§transparent-inline-closer} | A closer mid-heading is read as if it were not written. |
|
|
317
|
-
| {§executor-case} | An executor tag in any case. |
|
|
318
|
-
| {§executor-js-spelling} | When `node` is registered and `js` is not, `js` names `node` under {§executor-case}. |
|
|
319
|
-
| {§one-line-turn} | A whole turn on one line. |
|
|
320
|
-
| {§anchor-digits} | In a text scope, `@` followed by one to four digits cannot be a hash and is read as that line number, with… |
|
|
321
|
-
| {§unclosed-aside} | A heading whose aside opens with `<!--` and never closes on its line takes the rest of the line as the aside,… |
|
|
322
|
-
| {§quotation} | A fence that opens no operation quotes. |
|
|
323
|
-
| {§forgotten-tag} | A tag on the line under a bare fence is the operation. |
|
|
324
|
-
| {§executor-heading} | Syntax identifies an invocation; dispatch resolves its runtime. |
|
|
325
|
-
| {§interstitial-fence} | Only a heading under {§executor-heading} opens an operation. |
|
|
326
|
-
| {§closer-aside} | A closing fence followed on its line by one aside and nothing else is the closer; the aside is outside text. |
|
|
327
|
-
| {§heading-slot-order} | A heading near-miss with exactly one reading is read as that reading, with no diagnostic and no teaching… |
|
|
328
|
-
| {§native-tool-calls} | An emission that yields no operation may be a model's native tool-call markup naming a plurnk operation or a… |
|
|
329
|
-
| {§native-tool-call-receipt} | Unexecuted native markup draws a warning. |
|
|
330
|
-
| {§empty-section} | Both the compact bodyless form and an empty multiline block normalize optional bodies to null. |
|
|
331
|
-
| {§statement-rendering} | `PlurnkParser.stringify` renders native OP names and named runtime fences from the shared AST, with one blank… |
|
|
332
|
-
| {§slot-order} | Producers put target, scope, metadata, then aside, separated by one ASCII space. |
|
|
333
|
-
| {§lifecycle-slots} | NOTE accepts no target, scope, or metadata. |
|
|
334
|
-
| {§target-group} | READ and KILL take a target group. |
|
|
335
|
-
| {§safe-uri-target-groups} | An explicit URI list is a target group. |
|
|
336
|
-
| {§scope-on-scopeless} | A scope on an operation that takes none is dropped, and named. |
|
|
337
|
-
| {§heading-inline-body} | Nonempty body text belongs below the fence header. |
|
|
338
|
-
| {§bare-option-object} | A bare option object is the option block, where the house option array owns the block. |
|
|
339
|
-
| {§operation-aside} | The final header modifier may be one single-line HTML comment. |
|
|
340
|
-
| {§scheme-metadata-modifier} | A target may carry one single-line `[metadata]` block, placed under {§slot-order}; executor and SEND fences… |
|
|
341
|
-
| {§matcher-option} | `pattern` is the matcher, and it lives in the heading. |
|
|
342
|
-
<!-- /map -->
|
|
90
|
+
## §operation-fences 2. Canonical statement form and operation fences
|
|
343
91
|
|
|
344
92
|
`````text
|
|
345
93
|
```OP (path)? <scope>? [metadata]? <!-- aside -->?
|
|
346
94
|
body
|
|
347
95
|
```
|
|
348
96
|
|
|
349
|
-
```OP (path)? <scope>?
|
|
350
|
-
```
|
|
351
|
-
|
|
352
97
|
```executor (program-or-tool)?
|
|
353
98
|
input
|
|
354
99
|
```
|
|
355
100
|
`````
|
|
356
101
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
convention and never demanded ({§fence-closer}, {§closer-fallback}). There are no
|
|
361
|
-
operation suffixes or heading levels. Complete nested matches take precedence
|
|
362
|
-
over local recovery ({§balanced-fences}).
|
|
363
|
-
|
|
364
|
-
§fence-closer A block opened with N backticks closes at a line made of at least N backticks and
|
|
365
|
-
nothing else but horizontal whitespace, within three spaces of its line start
|
|
366
|
-
({§indented-fences}). Count follows CommonMark: a shorter fence inside the body is body; an equal
|
|
367
|
-
or longer bare fence may close the block or open a nested one, and {§fence-pairing} decides
|
|
368
|
-
which. The compact one-line form closes on its heading line after the modifiers under the same
|
|
369
|
-
rule.
|
|
370
|
-
|
|
371
|
-
§balanced-fences A complete nested interpretation takes precedence over missing-closer
|
|
372
|
-
recovery. Within a body, a line-leading labeled fence opens a literal block, and an equal or
|
|
373
|
-
wider bare fence nests unless closing there costs the reading less
|
|
374
|
-
({§fence-pairing}). A nested block ends at its first valid closer, as a CommonMark code block
|
|
375
|
-
does. So a wider outer fence holds any narrower body, finished or not, and equal widths nest
|
|
376
|
-
when every inner block closes. Preserve every nested body byte, including apparent operations
|
|
377
|
-
and executor invocations, without dispatching them. These rules apply equally to model programs,
|
|
378
|
-
stored programs, client operations, and reasoning quotations.
|
|
379
|
-
|
|
380
|
-
§operation-fences **Accept three or more backticks; teach and render three.** A line-start fence
|
|
381
|
-
of three or more backticks carrying an operation heading ({§executor-heading}) opens that
|
|
382
|
-
operation ({§indented-fences}), and no width draws a receipt. Three is the taught form and the
|
|
383
|
-
rendered form; a wider fence is the same statement, and it is how a body holding fence lines of
|
|
384
|
-
its own is held: an opener wider than any of them ({§balanced-fences}). Enclosing quotations
|
|
385
|
-
remain inert ({§quotation}), and a fence inside an intact block is never promoted to an
|
|
386
|
-
operation ({§fence-heading-in-body}). `plurnk.md` teaches exactly three and shows the wider
|
|
387
|
-
fence only as the nesting form. Every producer of a statement — the parser, a client composing
|
|
388
|
-
`/look`, a client's tab-completion — writes `PLURNK_FENCE` rather than its own literal
|
|
389
|
-
(plurnk/plurnk#92).
|
|
390
|
-
|
|
391
|
-
§naked-operation **A native name with only an optional aside opens without a fence.** A column-zero line outside any block containing a native operation's name and at most one complete `<!-- aside -->`, with only horizontal whitespace otherwise, opens that operation as if it were fenced with the taught three backticks. The aside retains its ordinary meaning: no target or modifiers are inferred. One warning-severity receipt follows a recovered statement — `` `NOTE` opened with no fence; the taught form is three backticks. `` One rule for every native operation: a naked `WAIT` parks, a naked `NOTE` takes its text, a naked `READ` meets the ordinary missing-target refusal. Executors are runtimes rather than operations, and reasoning is never read this way.
|
|
392
|
-
|
|
393
|
-
| Fact | Rule |
|
|
394
|
-
| --- | --- |
|
|
395
|
-
| The body | runs to a line that is exactly the name again, to the next heading ({§fence-heading-in-body}), or to the end of the turn; a fence inside naming nothing known is body; the block expects no closer, so its body is never cut back ({§closer-fallback}) |
|
|
396
|
-
| A closer the author wrote anyway | still not body: when the body's last line is a bare backtick fence that no other fence line in the body pairs with (an odd count of fence lines), that line is the block's closer and leaves the body, so `SEND`, then the answer, then a closing fence delivers the answer alone; an aside-bearing name inside a body is not a closer |
|
|
397
|
-
| Not the form | an incomplete or repeated aside, an operand, or other heading text does not qualify and remains the unfenced form ({§unfenced-operation}) |
|
|
398
|
-
|
|
399
|
-
§fence-heading-in-body Outside a complete nested block ({§balanced-fences}), a fence
|
|
400
|
-
line of three or more backticks carrying a heading under {§executor-heading}
|
|
401
|
-
is a heading. Inside an open block it either ends that block without closing it and opens the
|
|
402
|
-
next statement, or it is a literal example held by the block. {§fence-pairing} chooses between
|
|
403
|
-
the two. Fence lines of
|
|
404
|
-
fewer than three backticks are never headings ({§operation-fences}). Known executors are `sh`
|
|
405
|
-
plus what the host names in `ParseOptions.executors`.
|
|
406
|
-
|
|
407
|
-
§closer-fallback A block that ends at a heading or at the end of the input has no closer of its
|
|
408
|
-
own; its body is the whole span less one terminating line ending. Where a same-character fence
|
|
409
|
-
narrower than an outermost block stands where its closer belongs, {§fence-pairing} may read it
|
|
410
|
-
as that closer, at the cost of one repair; an unclosed EDIT yields to the native heading after it
|
|
411
|
-
({§unclosed-mutation-yields}). This carries no diagnostic: a missing closer is
|
|
412
|
-
never an admission failure, and {§unparsed-tail-boundary} is not involved.
|
|
413
|
-
|
|
414
|
-
`plurnk.md` shows every operation closed; this recovery is not taught. It sits at the quiet end of
|
|
415
|
-
the scale {§outside-text} describes: the model opened the operation correctly
|
|
416
|
-
and only failed to close it, so the harness reads what it plainly meant and says nothing.
|
|
417
|
-
A departure that small earns no correction — telling a model its closer was missing costs
|
|
418
|
-
a sentence in every future packet to fix something already fixed.
|
|
419
|
-
|
|
420
|
-
§fence-boundary Every fence line inside a body takes one of these meanings, chosen for the whole
|
|
421
|
-
input at once by {§fence-pairing}:
|
|
422
|
-
|
|
423
|
-
| Fence encountered inside a body | Meaning |
|
|
424
|
-
|---|---|
|
|
425
|
-
| Indented four spaces or more, or by a tab | Body ({§indented-fences}) |
|
|
426
|
-
| Fewer backticks than the block's own, or another character | Body, or the opener of a nested block |
|
|
427
|
-
| At least the block's backticks, bare | The block's closer, or the opener of a nested block |
|
|
428
|
-
| Labeled, without an operation heading | The opener of a nested block, or body |
|
|
429
|
-
| Three or more backticks with an operation heading ({§executor-heading}) | A heading that ends the block, or a literal example |
|
|
430
|
-
|
|
431
|
-
### §fence-pairing Fence pairing
|
|
432
|
-
|
|
433
|
-
A whole-input pass, `FencePairing`, decides where every block ends before the lexer reads a
|
|
434
|
-
token; the lexer's semantic predicates consult its decision, and ANTLR keeps framing, slots and
|
|
435
|
-
statement composition ({§parser-architecture}). Heading shape comes from the same grammar and
|
|
436
|
-
AST interpretation as normal admission, including recovered operand order and target notation;
|
|
437
|
-
fence pairing does not infer slot ownership from raw text. The pass is a least-cost parse of the fence
|
|
438
|
-
lines as a bracket language, in the manner of Aho and Peterson's least-errors parser
|
|
439
|
-
(SIAM J. Comput. 1(4), 1972): every reading of the fences is a derivation, and the pass returns
|
|
440
|
-
the cheapest.
|
|
441
|
-
|
|
442
|
-
§pairing-need **Why a pass ahead of the grammar.** Whether a bare fence closes a block or opens a
|
|
443
|
-
nested one depends on every fence after it, unboundedly. Measured on 10,486 distinct recorded
|
|
444
|
-
emissions:
|
|
445
|
-
|
|
446
|
-
| Alternative | Result |
|
|
447
|
-
|---|---|
|
|
448
|
-
| The fence structure as an ANTLR grammar under SLL prediction | 10,000 accepted, 0.2 ms mean; 486 not: 119 valid only under full LL, 243 needing repair, 113 unrepairable |
|
|
449
|
-
| The same grammar under full LL | A 267 KB packet echo took 4.8 s. Full-context results are not cached: the runtime records that caching them "was slower than interpreting and much more complicated" (`ParserATNSimulator`) |
|
|
450
|
-
| ANTLR's error recovery for the 356 that need repair | `DefaultErrorStrategy` repairs only at the point of detection, by single-token insertion or deletion; it has no least-cost repair over the input |
|
|
451
|
-
| Nongreedy lexer loops | A lexer decision is local, not "globally correct" (ANTLR `doc/wildcard.md`), so a nongreedy body ends at the first closer even when a later one is the block's |
|
|
452
|
-
| CommonMark's rule alone | The first closer of sufficient width closes (CommonMark 0.31.2 §4.5), and CommonMark declines to backtrack because it "would require backtracking… much less efficient". It cuts 110 recorded bodies at an inner bare block |
|
|
453
|
-
| A budgeted depth-first search over stacks | Exhausted its step budget on 20 recorded emissions of 188 to 3,501 fence lines |
|
|
454
|
-
|
|
455
|
-
The ALL(*) paper (Parr, Harwell, Fisher, OOPSLA 2014) states the limits relied on here: SLL either
|
|
456
|
-
behaves like LL or reports an error (Theorem 6.5, §3.2), and an ambiguity resolves to the lowest
|
|
457
|
-
alternative (§4.2), so neither mode chooses among complete readings by cost. The grammars-v4
|
|
458
|
-
Python (`INDENT`/`DEDENT`) and PHP (heredoc) grammars settle block structure the same way:
|
|
459
|
-
outside the grammar, then hand the lexer tokens.
|
|
460
|
-
|
|
461
|
-
§pairing-algorithm **Algorithm.** Each line is classified by the lexer's line-start rules: text, a bare fence, a labeled fence, a heading, a closer glued to a heading, or a naked operation name. Evaluation runs on an explicit work stack.
|
|
462
|
-
|
|
463
|
-
| Step | Rule |
|
|
464
|
-
| --- | --- |
|
|
465
|
-
| Summaries | a block's contents read the same whatever lies beneath it on the stack, so the pass summarizes each (position, block, what the block holds so far) once; for a nested block, the summary maps every place the block could end to its cheapest reading up to there, and parents compose their children's summaries; an outermost block is evaluated in the root's frame, where only the cost to the end of the input matters, so it holds one entry |
|
|
466
|
-
| Candidates | taken in preference order and replace one another only by costing strictly less, so each entry is the reading a preference-ordered exhaustive search returns |
|
|
467
|
-
| Closers | inside a quotation or a nested block the first valid closer closes, CommonMark's rule, applied as a disambiguation filter (Klint and Visser, 1994); an operation body keeps the choice between nesting and closing |
|
|
468
|
-
| A block opened by a bare fence | must hold a line, except a quotation at the top level: two stray fences in a row there are an empty quotation, shown and harmless, rather than two repairs that a reading could avoid only by hiding an operation |
|
|
469
|
-
|
|
470
|
-
§pairing-objective **Objective.** A complete reading, one that needs no repair, wins over every
|
|
471
|
-
repaired reading: a complete nested interpretation takes precedence. Each class has its own order.
|
|
472
|
-
|
|
473
|
-
| Counted | Complete readings | Repaired readings | Why |
|
|
474
|
-
|---|---|---|---|
|
|
475
|
-
| Hidden operations: a heading that will not run, written at its block's width or wider | 1, together with discarded code | 1 | Every operation the author wrote should run |
|
|
476
|
-
| 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 (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 |
|
|
477
|
-
| 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 shown as a code block inside a SEND, WORK, FORK or BARE body ({§prose-code-blocks}) | none: the author's enclosure wrote it as an example | 3, together with the row below | A heading the author wrapped is the example it was written as |
|
|
478
|
-
| Discarded code: a labeled fence in a body read as text, or such an executor heading read as text | 1, together with hidden operations | 3, together with the row above | Every code block the author declared should stand |
|
|
479
|
-
| Supplied closers | 2 | 4 | A block the author opened should end at a fence the author wrote |
|
|
480
|
-
| Other fences read as text | 3 | 5 | The least departure from the fences as written |
|
|
481
|
-
|
|
482
|
-
Complete readings compare as a single least-errors order over departures from the fences as written, and a heading the author enclosed in a wider or labeled fence is the example it was written as, not a departure.
|
|
483
|
-
|
|
484
|
-
| Rule | What |
|
|
485
|
-
| --- | --- |
|
|
486
|
-
| A narrower message fence | a message whose own fence is narrower than the fences wrapping its examples still holds them: the reading that lets a wrapper close the message and runs the examples ties on cost and loses on preference, since a body nests before it closes ({§balanced-fences}) |
|
|
487
|
-
| A broken shape | once every reading of an emission needs a repair, its shape is already broken, and saving a repair is not worth an operation the author wrote |
|
|
488
|
-
| The summary | keeps, for every place a block could end, the best complete reading under each order and the best repaired reading, so the search stays exact: a repaired total may take either class for any of its parts |
|
|
489
|
-
|
|
490
|
-
§unclosed-mutation-yields **A mutation body never swallows a native operation.** An example inside an EDIT is written with a wider outer fence ({§operation-fences}); a heading narrower than the EDIT's fence is literal by declaration and stays text. At the EDIT's own width a native operation heading runs, closed or not: read as the body's example it would be a *swallowed* operation, and a swallowed operation outranks any number of repairs — a reading that swallows one is never complete, and among repaired readings the fewest swallowed wins before every other class. Only EDIT mutates from its body, so only EDIT carries the rule: an executor heading inside an EDIT keeps the ordinary reading, and SEND, WORK, FORK and BARE bodies keep {§prose-code-blocks}.
|
|
491
|
-
|
|
492
|
-
| Shape | Reading |
|
|
493
|
-
| --- | --- |
|
|
494
|
-
| ```` ```EDIT (b.py) ```` follows ```` ```EDIT (a.py) ```` with no closer between | the reading that supplies `a.py`'s closer before that heading wins, both EDITs run, and no operation heading is written into a file |
|
|
495
|
-
| The same shape with a closer after the example | fence-identical, and reads the same way |
|
|
496
|
-
|
|
497
|
-
Equal cost goes to the earlier alternative: nesting before closing in an operation body,
|
|
498
|
-
closing before nesting in a quotation, and a supplied closer before a literal example at a
|
|
499
|
-
heading. An operation example quoted in a body of its own width therefore reads as the body
|
|
500
|
-
closing and the example running; the wider outer fence or the tab offset quotes it
|
|
501
|
-
({§operation-fences}).
|
|
502
|
-
|
|
503
|
-
§prose-code-blocks **A message or a prompt shows code.** Inside a `SEND`, `WORK`, `FORK` or `BARE`
|
|
504
|
-
body, at any depth, a heading that names an executor (` ```sh `, ` ```python3 `) is a code block the
|
|
505
|
-
text shows: it never ends the block and never runs. A native
|
|
506
|
-
operation's heading still ends such a block, since a message is often followed by the author's next
|
|
507
|
-
operation. In the distinct recorded benchmark emissions an executor heading ended an unclosed
|
|
508
|
-
`WORK` body 14 times, a `BARE` body 9 times and a `SEND` body twice; every one inspected (qflash
|
|
509
|
-
run218, run114 and run118, deepdumb run44) was the child's task, the prompt's code or code quoted in
|
|
510
|
-
a report, and reading it as an operation truncated the task and ran the example in the parent.
|
|
511
|
-
|
|
512
|
-
§message-run-on **A message runs to the end of the turn.** A `SEND`, `WORK`, `FORK` or `BARE`
|
|
513
|
-
whose block reaches the end of the input without its closer takes it
|
|
514
|
-
there with no repair: the text after its last inner block is still the deliverable, the message
|
|
515
|
-
or the task, never outside text. The run counts as a supplied closer, so a reading that closes at
|
|
516
|
-
a real fence still wins, and it never costs an operation: when the reading that runs on hides more
|
|
517
|
-
operations the author wrote than the best repaired reading, the repaired reading stands. Across the
|
|
518
|
-
11,235 distinct recorded benchmark emissions this changes 26 readings: bodies that had ended at an
|
|
519
|
-
inner fence keep what followed it (the qflash run192, run90 and run210 deliverables regain their last
|
|
520
|
-
sections), one echoed transcript (glm run155) runs one more operation, and none loses one.
|
|
521
|
-
|
|
522
|
-
§pairing-witness **Witnesses.**
|
|
523
|
-
|
|
524
|
-
| Witness | Result |
|
|
525
|
-
|---|---|
|
|
526
|
-
| Every input up to six lines over eleven line shapes, against a forward exhaustive search over explicit stacks under the same moves and class orders | 1,948,716 inputs, 0 differences in chosen cost; the same harness reports 37 differences at four lines when a summary keeps one entry per end regardless of class |
|
|
527
|
-
| The generated matrix: six operations × widths 3–5 × twelve body shapes × four tails | 864 of 864 (`fence-matrix.test.ts`); the four `EDIT · outer 3 · labeled quoted operation` cells read the example as an operation ({§unclosed-mutation-yields}), and in the `next operation` cell the EDIT's written closer, now a stray top-level fence, quotes the operation after it ({§quotation}) |
|
|
528
|
-
| 10,486 recorded emissions | All parse; 0.9 ms mean, 267 ms at most (3,501 fence lines). The work is polynomial in fence lines, cubic at worst, so no step bound is needed |
|
|
529
|
-
| The same, against the single-order objective | 10,466 identical; 19 read more operations, where headings follow unclosed blocks or a closer is glued to the next opener; 1 echoed transcript reads a different operation; none reads fewer |
|
|
530
|
-
|
|
531
|
-
§indented-fences **CommonMark's indentation, everywhere.** A fence line may follow at most three
|
|
532
|
-
spaces; four or more, or a tab, make it indented code. That one rule reads every fence purpose the
|
|
533
|
-
same way: an opener within three spaces opens, a closer within three closes, a heading within
|
|
534
|
-
three ends an unclosed block, and a body keeps its own lines' indentation. A fence indented
|
|
535
|
-
further is literal wherever it stands — prose outside a block, body inside one — and draws no
|
|
536
|
-
advisory: `plurnk.md` shows its own examples offset, so an offset fence is the page's own form
|
|
537
|
-
and there is no mistake to report. The parser presumes nothing about why a fence is offset.
|
|
538
|
-
Outside an operation, offset text is response text under
|
|
539
|
-
{§response-text}; inside a body, it stays literal body content.
|
|
540
|
-
|
|
541
|
-
§inline-chain A closer on a heading line, or on a body's closing line, may be
|
|
542
|
-
followed on that same line by the next opener; the closer still closes, and the
|
|
543
|
-
opener opens. This absorbs the habit of writing several operations in one
|
|
544
|
-
paragraph after prose. Prose after a closer on its line ends the chain; slot-shaped
|
|
545
|
-
text there is the heading's own and is read under {§transparent-inline-closer}.
|
|
546
|
-
|
|
547
|
-
§transparent-inline-closer **A closer mid-heading is read as if it were not written.** A
|
|
548
|
-
closing fence on a heading line followed by more of that heading — a `<scope>`, an
|
|
549
|
-
`[option block]`, a `<!-- aside -->`, or a naked matcher — does not end the reading: the
|
|
550
|
-
heading keeps taking its slots under {§trailing-slots} and {§naked-pattern}, exactly as though
|
|
551
|
-
the closer were absent, so ```` ```READ (a.md)``` ```` `<1,2>` is the same operation as `READ (a.md) <1,2>`.
|
|
552
|
-
The closer is still a closer: the block ends with that physical line and never reaches down for
|
|
553
|
-
the next operation, which is what a bare heading carrying a matcher would do. A closer followed
|
|
554
|
-
by the next opener is {§inline-chain}, and by nothing is the ordinary {§fence-closer}. There is
|
|
555
|
-
no ambiguity to resolve: a slot begins with `<` or `[` and an opener with a backtick run, so the
|
|
556
|
-
shapes are disjoint.
|
|
557
|
-
|
|
558
|
-
§executor-case **An executor tag in any case.** An executor heading matches a
|
|
559
|
-
known executor's name case-insensitively (`SH` opens `sh`); its AST carries the
|
|
560
|
-
registered spelling when known, otherwise the lowercase name. Native operation
|
|
561
|
-
names remain case-sensitive. Recognition follows {§executor-heading}; availability
|
|
562
|
-
and permission are checked at dispatch, not granted by a recognized spelling.
|
|
563
|
-
|
|
564
|
-
§executor-js-spelling When `node` is registered and `js` is not, `js` names
|
|
565
|
-
`node` under {§executor-case}. Parsing normalizes the runtime before admission
|
|
566
|
-
and dispatch; execution, policy, receipts, and output addresses remain Node's.
|
|
567
|
-
The authored source remains unchanged. This spelling adds no executor, discovery
|
|
568
|
-
entry, or model-facing teaching. An explicitly registered `js` retains its own
|
|
569
|
-
identity; absent `node`, the shorthand grants no executable capability.
|
|
570
|
-
|
|
571
|
-
§one-line-turn **A whole turn on one line.** A model may emit a turn as a single
|
|
572
|
-
line: prose, then heading after heading with no line ending anywhere. Two
|
|
573
|
-
rules absorb it. The next opener on a heading's own line, after the heading's
|
|
574
|
-
slots, ends that heading's block bodyless and opens ({§empty-section}), so
|
|
575
|
-
````EDIT (a.rs) <60,66> <!-- drop --> ```EDIT (b.rs) <31,38>` is two scoped
|
|
576
|
-
deletions. Literal inline bodies follow {§heading-inline-body}.
|
|
102
|
+
- **Backtick block boundaries**: Every canonical statement is a fenced block opened at column 0–3 by three or more backticks (`PLURNK_FENCE` is ``` ``` ```) followed on the same physical line by an operation keyword (`FIND`, `READ`, `EDIT`, `COPY`, `MOVE`, `SEND`, `BARE`, `WORK`, `FORK`, `KILL`, `NOTE`, `WAIT`) or a runtime executor name.
|
|
103
|
+
- **Fence width and nesting**: The parser accepts three or more backticks and renders three; a wider outer fence encloses narrower inner fences verbatim without dispatching them. A block closes at a line with at least the opening backtick count and only horizontal whitespace (or an optional closer aside), or falls back at the next outer operation heading or end of input.
|
|
104
|
+
- **Naked operation recovery**: A column-zero line outside any block containing only a native operation name and optional `<!-- aside -->` opens that operation with a warning-severity advisory naming three backticks as the taught form.
|
|
577
105
|
|
|
578
|
-
§
|
|
579
|
-
hash and is read as that line number, with one warning-severity advisory naming
|
|
580
|
-
the five-character anchor form. Five characters after `@` are always an anchor.
|
|
106
|
+
### §fence-pairing Fence pairing, quotations, and native tool calls
|
|
581
107
|
|
|
582
|
-
|
|
583
|
-
line takes the rest of the line as the aside, with one warning-severity advisory.
|
|
584
|
-
A closed aside followed by more text is unchanged.
|
|
108
|
+
`FencePairing` runs ahead of the lexer as a least-cost bracket parse over all fence lines in the turn so nested code blocks, unclosed blocks, and quoted examples resolve globally:
|
|
585
109
|
|
|
586
|
-
|
|
587
|
-
is not an operation heading ({§executor-heading}) — unlabeled or tagged like an unknown
|
|
588
|
-
code language (`ts`, `json`) without invocation slots — opens a
|
|
589
|
-
quotation that runs to its matching closer (same character, width at least the opener's) or to
|
|
590
|
-
the end of the input. Everything inside is data: no operation runs there and native tool-call
|
|
591
|
-
markup is not read ({§native-tool-calls}). An operation fenced inside an unlabeled code block
|
|
592
|
-
draws one warning that it was shown, not run. At the top level, a bare fence whose next line is an
|
|
593
|
-
operation heading opens that operation instead ({§forgotten-tag}). A labeled block is an example by
|
|
594
|
-
declaration, and nothing else inside draws an advisory.
|
|
595
|
-
|
|
596
|
-
§forgotten-tag **A tag on the line under a bare fence is the operation.** At the top level, a bare fence whose next line is an operation heading, a native name alone or with a slot or an executor's name with its operand, opens that operation as if both were written on one line: the heading takes its slots, the lines after it are its body, and it closes like any operation. It runs, and one warning-severity receipt names the form — `` `READ` ran, though its fence was malformed: the opening fence, OP, parameters, and aside share one line. `` Saying that it ran keeps the model from issuing it again. Inside a body the same lines stay literal. A bare fence alone draws nothing: nearly every one holds code or quoted output. An example the model means only to show belongs in a properly nested body ({§balanced-fences}) or a quotation ({§quotation}), so a model may show plurnk's own operations in an answer. Quotation outside an operation is response text under {§response-text}, not an executable program or a completion envelope.
|
|
597
|
-
|
|
598
|
-
| Draws nothing | Why |
|
|
110
|
+
| Rule | Contract |
|
|
599
111
|
| --- | --- |
|
|
600
|
-
|
|
|
601
|
-
|
|
|
602
|
-
|
|
|
603
|
-
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
fence, an unlabeled fence and an unknown tag without invocation slots all quote. Each is a shape a model reaches for from ordinary Markdown rather than from this
|
|
608
|
-
teaching, so honouring it protects an example the model already believed was safe
|
|
609
|
-
({§outside-text} places the scale). The offset is the one form that survives every
|
|
610
|
-
fence style.
|
|
611
|
-
|
|
612
|
-
§executor-heading **Syntax identifies an invocation; dispatch resolves its runtime.** A native
|
|
613
|
-
operation or a known executor name opens a heading. An otherwise unknown fence name also opens an
|
|
614
|
-
executor heading when the next non-horizontal-whitespace character starts an invocation slot:
|
|
615
|
-
`(` for the target, `[` for metadata, or `<` for scope (not `<!--`, an aside). The same slot grammar
|
|
616
|
-
then validates and binds it, including malformed-slot diagnostics and permitted slot ordering.
|
|
617
|
-
Availability does not affect that parse. Known executors are the host's supplied vocabulary plus
|
|
618
|
-
`sh`; knowing a name also permits its targetless, slotless form. Enclosing quotations, indented
|
|
619
|
-
examples and reasoning-channel restrictions remain unchanged.
|
|
620
|
-
|
|
621
|
-
§interstitial-fence Only a heading under {§executor-heading} opens an operation. Any other fence
|
|
622
|
-
opens no operation: outside a block it quotes under {§quotation}, inside a body it is body.
|
|
623
|
-
|
|
624
|
-
§closer-aside A closing fence followed on its line by one aside and nothing else
|
|
625
|
-
is the closer; the aside is outside text. Read as body, that line would be written
|
|
626
|
-
into the edited resource (#758). A
|
|
627
|
-
closing fence followed by any other text is still body.
|
|
628
|
-
|
|
629
|
-
§heading-slot-order A heading near-miss with exactly one reading is read as that
|
|
630
|
-
reading, with no diagnostic and no teaching (#758):
|
|
631
|
-
|
|
632
|
-
- an aside written before the heading's remaining scope or JSON option block is
|
|
633
|
-
read after them (`READ (a.md) <!-- why --> <1,-1>`); an aside followed by a
|
|
634
|
-
target or a non-JSON block keeps its place and stays refused;
|
|
635
|
-
- zero-width characters (U+200B–U+200D, U+2060, U+FEFF) on a heading line are skipped;
|
|
636
|
-
- a matcher that begins with a sigil and is quoted in single backticks
|
|
637
|
-
(`` `^def test_` ``) is that matcher; quoted text without a sigil stays refused.
|
|
638
|
-
|
|
639
|
-
Tokens keep their source positions; only their order in the stream changes.
|
|
640
|
-
|
|
641
|
-
§native-tool-calls **An emission that yields no operation may be a model's native tool-call markup naming a plurnk operation or a known executor, and every popular family is read the same way.** A block keeps its line count where its lines allow, so statement positions name the source line; an inline block grows to its fences. A call with an unknown name or parameter, or a FIND, READ, EDIT, COPY or MOVE naming no target, leaves the whole emission as it was; calls that cannot be mapped draw {§native-tool-call-receipt}. An emission that already yields an operation is never rewritten. No diagnostic, notice or teaching mentions a successful reading (#760).
|
|
642
|
-
|
|
643
|
-
| Family | Shape |
|
|
644
|
-
| --- | --- |
|
|
645
|
-
| DeepSeek | `<||DSML|| calls>` blocks of `invoke` elements with `parameter` children; DeepSeek writes the whole heading into the invoke's name — `<||DSML|| invoke name="FIND (docs/**) /Http404/ <!-- docs mentions -->">`, or `name="sh` with no closing quote and the script on the lines below — and the element's content is the body: the first word of the name is the operation, the rest its slots, and `parameter` tags around the content are debris, as are a copied facts line (`{"lines":6}`) under the heading, a `comment` attribute (the aside), and `<||DSML||tool_calls>` spelled without spaces |
|
|
646
|
-
| Anthropic-style | `<function_calls>` blocks of `invoke` elements with `parameter` children |
|
|
647
|
-
| The `<tool_call>` family | `<function=NAME>` with `<parameter=key>` children (Qwen, MiMo); a JSON object with `name` and `arguments` (Hermes and kin); a name followed by `<arg_key>`/`<arg_value>` pairs (GLM) |
|
|
648
|
-
| Llama | a bare `<function=NAME>` element; a `<\|python_tag\|>` JSON object |
|
|
649
|
-
| Mistral | a `[TOOL_CALLS]` JSON array |
|
|
650
|
-
| Kimi | `<\|tool_call_begin\|>` sections |
|
|
651
|
-
| Qwen's own shapes | a flat JSON call whose operation is the value of an `op`, `action` or `cmd` key in any case (`{"op": "READ", "path": …, "range": …}`); an XML element named by the operation (`<NOTE>…</NOTE>`, `<FIND (path) <1,3></FIND>`, `<read path="…"/>`) or by a generic `op` element or function (`<op op="READ" …>`, `<op verb="READ" …/>` in `<ops>`, `<op=READ (path) …>`, `<function=OP name="READ" …>`, `<function=OP>` around such a JSON call or a plurnk heading); a plurnk heading written as the call's text (`<tool_call>READ (a.py) <60,120>`, `=READ (…)`) |
|
|
652
|
-
|
|
653
|
-
| Call piece | Read as |
|
|
654
|
-
| --- | --- |
|
|
655
|
-
| `path`/`target`/`file_path`/`file`/`resource`/`uri` | the target |
|
|
656
|
-
| `scope`/`range`/`lines`, `start`/`end` written apart, `offset`/`limit` (a first line and a count), a JSON array (`[500,530]`) or a `{"start", "end"}` object | the scope; plurnk slots written after the name are kept, and a trailing scope's own bracket may close the tag |
|
|
657
|
-
| `pattern`/`regex`/`query` | a matcher option |
|
|
658
|
-
| `aside`, `reason`, `description` | the aside |
|
|
659
|
-
| `body`/`content`/`command`/`text`/`input` or plain text inside the call; an executor's string `input` | the body, an executor's program |
|
|
660
|
-
| An executor's unknown parameter | one of its options, `[{"maxTokens": "2500"}]`, for its owner to accept or refuse; `bash` names `sh` when `sh` is the registered shell |
|
|
661
|
-
| Closing tags left over from another family (`</parameter>`, `</invoke>`, `</>`) after a call | markup noise |
|
|
662
|
-
|
|
663
|
-
Bare `invoke` elements use the same slots as wrapped calls. A complete JSON object
|
|
664
|
-
inside an invoke supplies arguments when no explicit parameter elements are
|
|
665
|
-
present; parameter bodies remain literal. Unknown native-operation arguments
|
|
666
|
-
are not guessed.
|
|
667
|
-
|
|
668
|
-
§native-tool-call-receipt **Unexecuted native markup draws a warning.** After exact
|
|
669
|
-
recovery, the first remaining native-call block in outside text receives one
|
|
670
|
-
warning naming its markup and the fenced form. It does not invalidate real OPs
|
|
671
|
-
beside it, become an operation, or independently earn a strike. A turn with no
|
|
672
|
-
operation follows {§empty-turn}. Quoted examples and operation bodies are not
|
|
673
|
-
attempted calls. Outside text remains unchanged evidence under {§outside-text}.
|
|
674
|
-
|
|
675
|
-
§empty-section Both the compact bodyless form and an empty multiline block
|
|
676
|
-
normalize optional bodies to null. Closing fences are conventional, never required
|
|
677
|
-
({§closer-fallback}).
|
|
678
|
-
|
|
679
|
-
§statement-rendering `PlurnkParser.stringify` renders native OP names and named
|
|
680
|
-
runtime fences from the shared AST, with one blank line between operations. A
|
|
681
|
-
target group compiles to ordinary statements, each rendered with its resolved
|
|
682
|
-
selection ({§target-group}).
|
|
683
|
-
Every closing fence occupies its own line, including bodyless operations;
|
|
684
|
-
inline fences remain accepted input, not generated examples.
|
|
685
|
-
It chooses at least three backticks and more than any run within the body
|
|
686
|
-
({§balanced-fences}), preserving body bytes on reparse. Fence length is syntax,
|
|
687
|
-
not AST or persistence state. Core-authored programs use
|
|
688
|
-
this serializer and the ordinary admission parser. Rendering preserves a matcher
|
|
689
|
-
beside owner metadata, not only a matcher carried inside its `pattern` option.
|
|
690
|
-
|
|
691
|
-
| Element | Contract |
|
|
692
|
-
|---|---|
|
|
693
|
-
| Fence name | Reserved native OP, otherwise an executor name under {§executor-heading} |
|
|
694
|
-
| `(path)` | Target/program/tool slot; COPY and MOVE each have two resource operands |
|
|
695
|
-
| `[metadata]` | One owner-interpreted block; options, never the op's input |
|
|
696
|
-
| `<scope>` | Operation-specific numeric or anchored coordinates |
|
|
697
|
-
| `<!-- … -->` | Optional final, single-line aside |
|
|
698
|
-
| Body | Literal content between framing newlines |
|
|
699
|
-
| Closing fence | The opening backtick count, on its own line |
|
|
700
|
-
|
|
701
|
-
§slot-order Producers put target, scope, metadata, then aside, separated
|
|
702
|
-
by one ASCII space. Target and scope form one resource selection; COPY/MOVE
|
|
703
|
-
repeat the complete selection/metadata group per operand. ANTLR accepts
|
|
704
|
-
adjacent slots and scope/metadata permutations within a selection without
|
|
705
|
-
changing ownership or making them distinct canonical forms. Metadata may also
|
|
706
|
-
precede the first target, belonging to that first selection. Between targets it
|
|
707
|
-
belongs to the preceding selection, never the following one. Single-target
|
|
708
|
-
scope-first tolerance remains valid with metadata before or after that scope.
|
|
709
|
-
Each selection has at most one scope; its metadata blocks retain their authored order. A second scope
|
|
710
|
-
is refused; the recovery is the scope's working form ({§parse-recovery}).
|
|
711
|
-
|
|
712
|
-
§lifecycle-slots NOTE accepts no target, scope, or metadata. WAIT retains its
|
|
713
|
-
optional target and duration under {§send-wait-scope}. Their literal bodies begin
|
|
714
|
-
below the header.
|
|
715
|
-
|
|
716
|
-
§target-group **READ and KILL take a target group.** A READ or KILL heading may carry several `(path)` slots, each binding the scope and metadata that follow it under {§slot-order}; a naked pattern on the heading ({§naked-pattern}) supplies the default for each member without its own metadata `pattern`. The form is accepted, not taught: it rescues a bulk attempt written one path per slot.
|
|
717
|
-
|
|
718
|
-
| Fact | Rule |
|
|
719
|
-
| --- | --- |
|
|
720
|
-
| Compilation | the parser compiles the complete heading into one ordinary statement per member, in authored order, each with its own target, scope, metadata and resolved matcher; no duplicated first-member or group representation crosses the parser boundary |
|
|
721
|
-
| Position and aside | every resulting statement retains the heading's source position and aside; a heading-level parse error refuses all its members, not valid sibling headings |
|
|
722
|
-
| A distilling body ({§log-kill-distillation}) | lands once, with the first member |
|
|
723
|
-
| Other operations | COPY and MOVE keep their two operands with their own meaning ({§transfer-resource-selections}); every other operation takes one slot, and a second slot on its heading is the slot-opener diagnostic |
|
|
724
|
-
|
|
725
|
-
§safe-uri-target-groups **An explicit URI list is a target group.** In READ and KILL only,
|
|
726
|
-
one path slot may contain several explicit `scheme://` URIs separated by top-level commas or
|
|
727
|
-
whitespace; braces protect their contents. Every member must independently parse as a URI,
|
|
728
|
-
otherwise the target stays singular and exact. Local filenames containing spaces or commas
|
|
729
|
-
are never split. Each URI inherits its slot's scope and metadata and compiles through
|
|
730
|
-
{§target-group}; COPY/MOVE and other operations do not admit this tolerance.
|
|
731
|
-
|
|
732
|
-
§scope-on-scopeless **A scope on an operation that takes none is dropped, and named.** WORK, FORK,
|
|
733
|
-
BARE and NOTE take no scope, and neither does a SEND without a recipient; a `<…>` slot on such a
|
|
734
|
-
heading, in any position, is skipped and the operation runs, with one warning-severity advisory
|
|
735
|
-
naming the operation's slots and the dropped scope — `` `WORK` takes a target only; the scope
|
|
736
|
-
`<1,-1>` was ignored. `` (`` `NOTE` takes no target or
|
|
737
|
-
scope; … ``, `` `SEND` without a recipient takes no scope; … ``). An aside is never a scope, a
|
|
738
|
-
recipient SEND keeps its scope for the recipient ({§send-directed-scope}), and WAIT follows
|
|
739
|
-
{§send-wait-scope}. There is no ambiguity: the operation has one reading with or without
|
|
740
|
-
the slot.
|
|
741
|
-
|
|
742
|
-
§heading-inline-body Nonempty body text belongs below the fence header. Text after the slots
|
|
743
|
-
on the heading line is read by the operation's shape, by the builder that reads that operation,
|
|
744
|
-
and that reading is named: no text on the line is dropped silently, and none loses the operation.
|
|
745
|
-
Taken as the body, it is the first body line, with one warning: "`EDIT` body text was on the OP
|
|
746
|
-
line and was taken as the body; body content goes immediately beneath the opening fence line." A
|
|
747
|
-
sigil claims a matcher only on an operation that has one. This does not change the meaning of a
|
|
748
|
-
compact empty block or permit unmatched fences.
|
|
749
|
-
|
|
750
|
-
| Operation | Text after the slots on the heading line |
|
|
751
|
-
|---|---|
|
|
752
|
-
| FIND, READ, KILL with a non-log target | The matcher ({§naked-pattern}). Beside an option block's `pattern`, ignored ({§matcher-body-redirect}). |
|
|
753
|
-
| KILL on a log target | The matcher. Beside an option block's `pattern`, the first line of the distillation ({§log-kill-distillation}). |
|
|
754
|
-
| EDIT | A sigil-led matcher ({§edit-pattern}), unless an option block carries the `pattern`; otherwise the body. |
|
|
755
|
-
| COPY, MOVE | Ignored ({§transfer-resource-selections}). |
|
|
756
|
-
| SEND, WORK, FORK, BARE | One JSON object is the option block where the heading admits one ({§bare-option-object}); otherwise the body. |
|
|
757
|
-
| executors | The input body, including an unbracketed JSON object; metadata requires brackets. |
|
|
758
|
-
| NOTE, WAIT, KILL without a target | The body. |
|
|
759
|
-
|
|
760
|
-
§bare-option-object **A bare option object is the option block, where the house option array owns the block.** Executor headings always read an unbracketed object as input under {§heading-inline-body}, regardless of registration or input media type; their metadata is only the bracketed slot ({§scheme-metadata-modifier}).
|
|
761
|
-
|
|
762
|
-
| Heading | `{…}` after the slots is |
|
|
763
|
-
| --- | --- |
|
|
764
|
-
| SEND, BARE, WORK and FORK, where the heading admits a block (BARE, WORK and FORK only after their target) | read as `[{…}]`: the AST carries the array form, the written heading renders it, and one warning-severity receipt names the indulgence in place of the {§heading-inline-body} advisory: "`SEND` took a bare option object; the taught form is `[{…}]`." |
|
|
765
|
-
| One that already carries a block | inline body |
|
|
766
|
-
| FIND, READ and KILL | the matcher ({§naked-pattern}), because a search for JSON text is legitimate; a bare matcher that parses as a JSON object draws one advisory saying so: "`{…}` was read as the matcher." ({§diagnostic-observation}) |
|
|
767
|
-
|
|
768
|
-
§operation-aside The final header modifier may be one single-line HTML
|
|
769
|
-
comment. AstBuilder removes its delimiters and surrounding whitespace into
|
|
770
|
-
`aside: string | null`. It is durable descriptive text, not authority,
|
|
771
|
-
routing, timing, or body input. Comments inside a body remain literal.
|
|
772
|
-
|
|
773
|
-
§scheme-metadata-modifier A target may carry one single-line `[metadata]`
|
|
774
|
-
block, placed under {§slot-order}; executor and SEND fences also admit it without a target.
|
|
775
|
-
The block belongs to the selected scheme or executor, which owns its shape,
|
|
776
|
-
interpretation, validation and authority. The language assigns no meaning to
|
|
777
|
-
the content and stores each block's exact inner text: balanced brackets inside
|
|
778
|
-
the block are retained, and double-quoted strings protect their brackets.
|
|
779
|
-
Brackets inside `(path)` remain ordinary path and
|
|
780
|
-
glob characters. A block the owner cannot read, or a second block on one
|
|
781
|
-
operand, is the owner's `400`, never a parser diagnostic. An unfinished block
|
|
782
|
-
or multiline metadata loses its boundary.
|
|
783
|
-
|
|
784
|
-
**House policy, not a language rule:** every first-party scheme and executor
|
|
785
|
-
reads its block through the shared `MetadataOptions` reader, which takes the
|
|
786
|
-
block with its brackets as a JSON array of option objects, merged left to
|
|
787
|
-
right with later keys winning. Reply SEND additionally owns a numeric completion
|
|
788
|
-
element ({§message-completion}); WAIT owns its numeric duration ({§send-wait-scope}).
|
|
789
|
-
A third-party owner may read its block any way
|
|
790
|
-
it likes — the language guarantees only the exact inner text. Documentation
|
|
791
|
-
for a first-party owner therefore shows the bracketed array form.
|
|
792
|
-
|
|
793
|
-
Two keys never reach an owner:
|
|
794
|
-
`pattern`, the language's own ({§matcher-option}), and `env`, reserved for the
|
|
795
|
-
service's environment option on the operations that open a process or a
|
|
796
|
-
Worker; the shared reader withholds both from the owner's options.
|
|
797
|
-
|
|
798
|
-
§matcher-option **`pattern` is the matcher, and it lives in the heading.** On FIND, READ, KILL, EDIT, and each COPY/MOVE operand, the option `[{"pattern": "<matcher>"}]` carries the matcher string: the leading prefix claims its dialect under {§matcher-prefix-claims}, and AstBuilder lifts it into the statement's `matcher` (`MatcherBody | null`), positioned dialect errors included. The heading line is the matcher's home ({§naked-pattern}).
|
|
799
|
-
|
|
800
|
-
| Fact | Rule |
|
|
801
|
-
| --- | --- |
|
|
802
|
-
| The block | one that carries only `pattern` leaves `metadata: null` for the owner; beside other keys the block stays with the owner verbatim, and the owner's reader treats `pattern` as reserved; the language lifts only from one block that parses as a JSON array of objects, and anything else lifts nothing and reaches the owner's `400` untouched |
|
|
803
|
-
| Diagnostics | a `pattern` that is present but not a string is the language's own positioned diagnostic, as is a matcher of a claimed dialect that fails admission |
|
|
804
|
-
| Bodies | FIND and READ bodies become memory under {§retrieval-annotation}; a non-log KILL's body is unused and receives the advisory under {§matcher-body-redirect}; EDIT keeps its literal body: with a matcher it is the replacement for every selected span ({§edit-pattern}), and an absent body deletes them |
|
|
805
|
-
| `PlurnkParser.stringify` | writes a lifted matcher whose block left no metadata back bare when the bare form reads back identically and draws no advisory ({§naked-pattern}), otherwise as `[{"pattern": "…"}]` |
|
|
806
|
-
|
|
807
|
-
## 3. Lexical elements
|
|
808
|
-
|
|
809
|
-
| Element | Shape or role |
|
|
810
|
-
|---|---|
|
|
811
|
-
| Native OP | `FIND READ EDIT COPY MOVE SEND BARE WORK FORK KILL NOTE WAIT` |
|
|
812
|
-
| Executor name | Letters, digits, `_`, `.`, `+`, or `-`; reserved OPs win |
|
|
813
|
-
| Fence | Three or more backticks, matched by exact count |
|
|
814
|
-
| `(path)` | Local path, URI, program or tool name; §5 |
|
|
815
|
-
| `[metadata]` | One owner-interpreted block of owner-defined options |
|
|
816
|
-
| `<scope>` | Numeric or anchored coordinates; §7 |
|
|
817
|
-
| Body | Literal text; never recursively interpreted as operations |
|
|
818
|
-
|
|
819
|
-
## §op-shapes 4. Per-operation semantics
|
|
820
|
-
|
|
821
|
-
<!-- map -->
|
|
822
|
-
| Tag | Promise |
|
|
823
|
-
| --- | --- |
|
|
824
|
-
| {§retrieval-annotation} | A retrieval's authored body is memory, not selection or output. |
|
|
825
|
-
| {§note-value} | NOTE retains its literal body as ordinary model-owned working memory. |
|
|
826
|
-
| {§reasoning-operations} | The shared fence parser admits complete, line-leading NOTE, FIND and READ statements from exposed provider… |
|
|
827
|
-
| {§exec-executor-slot} | The fence name selects the executor directly: for example, `python3 (tools/report.py)` or `gitea… |
|
|
828
|
-
| {§turn-disposition} | WAIT requests parking; its literal body does not control scheduling, and its optional target is the label the… |
|
|
829
|
-
| {§send-wait-scope} | WAIT's optional `[seconds]` metadata is a non-negative finite scalar bounding this park, not the lifetime of… |
|
|
830
|
-
| {§send-directed-scope} | A recipient SEND carries an optional numeric scope after its target and metadata through to the addressed… |
|
|
831
|
-
| {§kill-scope} | KILL takes an optional text-coordinate scope beside its target, and an optional matcher. |
|
|
832
|
-
| {§bracket-metadata-slot} | Brackets are the owner's metadata modifier: the runtime or MCP service is the fence name, and tool input… |
|
|
833
|
-
| {§text-scope-semantics} | Exact regions use 1-based lines and Unicode code-point columns. |
|
|
834
|
-
| {§bare-statement} | BARE requests one isolated model inference. |
|
|
835
|
-
| {§read-find-normalization} | An authored READ is never rewritten into a FIND. |
|
|
836
|
-
| {§local-path-fragment} | A bare path takes `#channel` like a URL. |
|
|
837
|
-
| {§read-exact-target} | READ targets one exact resource (a local path or scheme URL, with optional `#channel` fragment or… |
|
|
838
|
-
| {§unscoped-edit-create-only} | No scope and an existing target is refused. |
|
|
839
|
-
| {§empty-mutation-scope} | Empty mutation content has one writable position: `<0>`, `<1>`, `<-1>`, and `<1,-1>` all insert the body as… |
|
|
840
|
-
| {§transfer-resource-selections} | COPY and MOVE require two singular `ResourceSelection` operands, source first and destination second, and… |
|
|
841
|
-
| {§find-result-unit} | For FIND, target selection fixes the paginated result unit after scheme address resolution… |
|
|
842
|
-
| {§copy-move-observation} | COPY and MOVE log projections preserve both admitted operand selections, including their independent scopes,… |
|
|
843
|
-
<!-- /map -->
|
|
844
|
-
|
|
845
|
-
The model-facing forms below are the canonical projection. Parser tolerance is
|
|
846
|
-
governed by {§canonical-statement}; runtime conditions remain explicit below.
|
|
847
|
-
|
|
848
|
-
| OP | `(path)` | `<scope>` | `body` |
|
|
849
|
-
|------|----------------------------------------------|---------------------------------|--------------------------------|
|
|
850
|
-
| FIND | required target or glob | optional result range | annotation normalized to NOTE |
|
|
851
|
-
| READ | required target | optional text region | annotation normalized to NOTE |
|
|
852
|
-
| EDIT | required file or entry | required for an existing target | literal text |
|
|
853
|
-
| COPY | required source and destination | optional region after each path | empty |
|
|
854
|
-
| MOVE | required source and destination | optional region after each path | empty |
|
|
855
|
-
| execution | the fence name is the runtime; optional program/tool path ({§exec-executor-slot}) | none ({§exec-lifetime}) | optional program input |
|
|
856
|
-
| BARE | optional prompt resource | none | prompt; optional with a path |
|
|
857
|
-
| WORK | optional fresh `worker://name`, or a prompt resource ({§worker-spawn-prompt-resource}) | none | prompt; optional with a resource |
|
|
858
|
-
| FORK | optional context-inheriting `worker://name`, or a prompt resource | none | prompt; optional with a resource |
|
|
859
|
-
| 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 |
|
|
860
|
-
| SEND | optional recipient | recipient-defined; none for workers ({§send-directed-scope}) | message |
|
|
861
|
-
| NOTE | none | none | literal working memory |
|
|
862
|
-
| WAIT | optional label ({§send-wait-scope}) | none; duration is `[seconds]` metadata | explanation of the wait |
|
|
863
|
-
|
|
864
|
-
§retrieval-annotation **A retrieval's authored body is memory, not selection or output.**
|
|
865
|
-
A nonblank body beneath FIND or READ becomes one ordinary NOTE immediately before
|
|
866
|
-
the retrieval, including a body consisting of a pattern or an HTML comment. A
|
|
867
|
-
grouped READ contributes one NOTE before its expanded statements. The retrieval
|
|
868
|
-
itself has no body; its selector comes only from its header. Normalization is the
|
|
869
|
-
same in content and exposed reasoning and never executes quoted body text. The
|
|
870
|
-
canonical rendering is NOTE followed by bodyless retrieval; exact submitted text
|
|
871
|
-
remains source evidence. The NOTE persists independently of retrieval success and
|
|
872
|
-
is curated normally. Explicit NOTE remains the taught form.
|
|
873
|
-
|
|
874
|
-
§note-value NOTE retains its literal body as ordinary model-owned working memory.
|
|
875
|
-
It has no target, scope, metadata, or lifecycle effect. Its full body participates
|
|
876
|
-
in ordinary log token accounting and model-driven curation; prior notes are not
|
|
877
|
-
automatically hidden. A continuing or parked turn that carried no NOTE is told so in
|
|
878
|
-
the next packet ({§continued-without-note}); the omission is never a failure. A
|
|
879
|
-
NOTE-only turn does not request completion; ordinary repetition and strike rules
|
|
880
|
-
still apply.
|
|
881
|
-
|
|
882
|
-
§reasoning-operations The shared fence parser admits complete, line-leading NOTE,
|
|
883
|
-
FIND and READ statements from exposed provider reasoning. Admission is part of
|
|
884
|
-
the language. Other backtick or tilde code blocks are opaque; heading and
|
|
885
|
-
missing-closer recovery cannot escape them or complete an unfinished reasoning
|
|
886
|
-
operation. Blockquoted and inline examples are not headings. Other reasoned
|
|
887
|
-
operations never execute.
|
|
888
|
-
|
|
889
|
-
Operations are admitted only after the provider response completes; their
|
|
890
|
-
presence never interrupts generation. Selected operations precede the content
|
|
891
|
-
program, in source order without deduplication, and use ordinary dispatch,
|
|
892
|
-
permissions, persistence and log
|
|
893
|
-
projection. Every admitted reasoning operation is the turn's work for admission and
|
|
894
|
-
continuation, a NOTE exactly as in the content program ({§note-value}); a turn that landed one is never empty. Reasoning
|
|
895
|
-
and content remain separate, unchanged forensic sources. Rejected or superseded
|
|
896
|
-
provider attempts execute none of their operations. All three operations remain
|
|
897
|
-
available in the ordinary program; no task inventory is inferred from reasoning.
|
|
898
|
-
Builder normalization warnings for admitted reasoning operations surface as
|
|
899
|
-
ordinary non-striking notices. Quoted, unfinished, malformed or disallowed
|
|
900
|
-
reasoning operations contribute neither execution nor normalization notices.
|
|
901
|
-
|
|
902
|
-
§exec-executor-slot The fence name selects the executor directly: for example,
|
|
903
|
-
`python3 (tools/report.py)` or `gitea (issue_list)` on the opening fence line.
|
|
904
|
-
Reserved native OP names take precedence. An executor heading's AST ({§executor-heading})
|
|
905
|
-
carries the `runtime` tag and no operation keyword, then `target`, metadata, timing and body fields.
|
|
906
|
-
Registration is checked at the invocation's execution point, after earlier operations settle
|
|
907
|
-
({§op-execution-order}), not by the syntax parser. An attached
|
|
908
|
-
MCP service uses that executor path and its owner validates the named tool and
|
|
909
|
-
input-body JSON against its schema. Unknown names do not fall back to a shell.
|
|
910
|
-
There is no runtime-less form: every execution names its runtime, and canonical
|
|
911
|
-
shell examples name `sh` explicitly.
|
|
912
|
-
The path names a program or tool and is never split. Metadata such as
|
|
913
|
-
`[{"cwd": "…"}]` remains interpreted by the selected executor.
|
|
914
|
-
|
|
915
|
-
§turn-disposition WAIT requests parking; its literal body does not control
|
|
916
|
-
scheduling, and its optional target is the label the row keeps, never a join;
|
|
917
|
-
the AST retains the normalized metadata duration as `seconds`, separate from text coordinates.
|
|
918
|
-
A turn admits any number of WAITs, all deferred until its other operations
|
|
919
|
-
settle and together one park. End-of-program adjudication owns continuation, joining and completion
|
|
920
|
-
under {§wait-obligation-matrix}. SEND carries optional message-completion metadata
|
|
921
|
-
under {§message-completion}. KILL requires a target; NOTE retains memory.
|
|
922
|
-
|
|
923
|
-
§send-wait-scope WAIT's optional `[seconds]` metadata is a non-negative finite scalar bounding
|
|
924
|
-
this park, not the lifetime of its work. Without it, the service supplies its
|
|
925
|
-
configured bound ({§worker-wait-timing}). Repeated duration slots select the
|
|
926
|
-
smallest valid scalar. Zero continues without parking or a diagnostic. Invalid
|
|
927
|
-
duration metadata is ignored with a terse warning; when none is valid, the configured
|
|
928
|
-
bound applies. A text scope cannot supply a duration and receives the same warning.
|
|
929
|
-
The optional target is a label, never a selected wake source;
|
|
930
|
-
the body is the wait's explanation. Neither creates a schedule or restricts ordinary
|
|
931
|
-
wake events. Ordinary malformed-header rules still apply. Multiple WAITs request
|
|
932
|
-
one park with the earliest bound, including the configured bound for each bare WAIT.
|
|
933
|
-
|
|
934
|
-
§send-directed-scope A recipient SEND carries an optional numeric scope after
|
|
935
|
-
its target and metadata through to the addressed owner, which assigns its
|
|
936
|
-
semantics or refuses it; worker actors refuse one (`scope-unsupported`, 400). A
|
|
937
|
-
targetless message takes no scope. A scope never changes the message body or
|
|
938
|
-
disposition.
|
|
939
|
-
|
|
940
|
-
§kill-scope **KILL takes an optional text-coordinate scope beside its target, and an optional matcher.** The scope is numeric or anchored (```` ```KILL (log:///**/READ) <17,-1>``` ```` or ```` ```KILL (worker:///notes.md) <@aB3dE,@0Aa9Z>``` ````); the matcher option selects rows or lines (```` ```KILL (log:///**) [{"pattern": "~stale"}]``` ````, {§matcher-option}). Each operand becomes an ordinary KILL statement with its own `target`, `lineMarker` and `matcher`; no grouped AST reaches execution ({§target-group}). KILL requires a target.
|
|
941
|
-
|
|
942
|
-
| Fact | Rule |
|
|
943
|
-
| --- | --- |
|
|
944
|
-
| The body | retained on a `log:///` target, where it is the model's distillation of what the KILL retires and lands as its own NOTE row ({§log-kill-distillation}); every other targeted KILL has `body: null`, and a body beneath one draws the advisory ({§matcher-body-redirect}); beneath a `log:///` KILL the body is never read as a matcher, while an inline pattern on the heading line still lifts |
|
|
945
|
-
| Without a scope | KILL retires or deletes the whole target |
|
|
946
|
-
| With one | it removes exactly that span — of a log body's packet projection or of an entry's content; Core owns the one-way semantics: there is no operation that restores a scoped-away log body |
|
|
947
|
-
|
|
948
|
-
§bracket-metadata-slot Brackets are the owner's metadata modifier: the runtime
|
|
949
|
-
or MCP service is the fence name, and tool input belongs in the body. A bracket
|
|
950
|
-
block is metadata before or after the target, with ownership under {§slot-order};
|
|
951
|
-
its position does not select an executor or change its interpretation.
|
|
952
|
-
|
|
953
|
-
The `<scope>` slot is optional where admitted and its domain is OP-specific. FIND
|
|
954
|
-
scopes ordered results. Executions and SEND scope owner-defined timing. READ, EDIT, COPY,
|
|
955
|
-
MOVE, and KILL use one universal text algebra independent of mimetype; a log
|
|
956
|
-
KILL admits only its one- and two-line forms for canonical log-body visibility:
|
|
957
|
-
|
|
958
|
-
| Arity | Surface meaning | Endpoint rule |
|
|
959
|
-
|---------------|---------------------------------------------------------------------|------------------------------------------------------|
|
|
960
|
-
| one integer | One whole physical line, or the documented `0`/`-1` mutation anchor | Exactly one ordinal line |
|
|
961
|
-
| two integers | Whole physical lines `firstLine..lastLine` | Both lines are included |
|
|
962
|
-
| four integers | Exact `startLine,startColumn,endLine,endColumn` region | Start included, end excluded; equality is zero-width |
|
|
963
|
-
|
|
964
|
-
§text-scope-semantics **Exact regions use 1-based lines and Unicode code-point columns.** One- and two-integer line selections normalize to the same exclusive-end `TextRegion` used by four-coordinate selections. Whole-line replacement deliberately accounts for newline separators; it is an ergonomic projection over exact replacement, not a different mimetype navigation mode. An end bound beyond the available content clamps to the final addressable endpoint; the start bound must resolve. Other arities and decimal text coordinates are runtime 416 failures.
|
|
965
|
-
|
|
966
|
-
| `-1` as | Means |
|
|
967
|
-
| --- | --- |
|
|
968
|
-
| A one-integer mutation scope | the append point |
|
|
969
|
-
| The end of an inclusive range | through the last line |
|
|
970
|
-
| An exact region's start or end line | the end of the content, so `<1,1,-1,1>` is the whole text and `<-1,1,-1,1>` appends |
|
|
971
|
-
| A column | the end of its line, so `<3,1,3,-1>` is line 3 to its end and `<3,-1,3,-1>` appends to that line |
|
|
972
|
-
|
|
973
|
-
An unadvertised ingestion tolerance: the runtime accepts three integers as `startLine,startColumn,endLine` and immediately normalizes them to the complete four-coordinate region ending after the final code point of `endLine`; producers never emit that form.
|
|
974
|
-
|
|
975
|
-
§bare-statement **BARE requests one isolated model inference.** Its optional
|
|
976
|
-
path names a prompt resource; its body supplies inline prompt text. At least
|
|
977
|
-
one must supply nonempty text at execution. With both, the complete resource
|
|
978
|
-
text precedes the body, separated by two newlines. The target's scheme owns any
|
|
979
|
-
metadata modifier. No scope, persistent worker identity, or output-language
|
|
980
|
-
shape is represented. Provider selection, source admission, batching,
|
|
981
|
-
accounting, and observation timing belong to the consuming service.
|
|
982
|
-
|
|
983
|
-
§read-find-normalization An authored READ is never rewritten into a FIND. A
|
|
984
|
-
glob target on READ keeps its glob, and the runtime fans it out into one exact
|
|
985
|
-
READ per matching path, with the authored scope and matcher ({§read-fan-out}
|
|
986
|
-
in the core SPEC). A model
|
|
987
|
-
that asks to read every file under a glob gets those files, bounded by the
|
|
988
|
-
FIND page and the preview scope, not a catalog it did not ask for. A matcher
|
|
989
|
-
never changes the operation either: READ with a `pattern` on an exact target
|
|
990
|
-
stays READ and renders the selected lines ({§read-pattern}). The survey of
|
|
991
|
-
paths is FIND, and only FIND.
|
|
992
|
-
|
|
993
|
-
§local-path-fragment **A bare path takes `#channel` like a URL.** `data/users.html#readable`
|
|
994
|
-
parses as `{ kind: "local", raw: "data/users.html", fragment: "readable" }`: the first `#` ends
|
|
995
|
-
the path and the rest is the channel (a spelling that opens with `#` names no path and stays
|
|
996
|
-
whole), exactly as `worker:///a.html#readable` decomposes, so
|
|
997
|
-
the `#channel` a READ receipt advertises (`channels: {"#readable": N}`) is addressable in the
|
|
998
|
-
same bare spelling the receipt used: a model that appends the channel to the path it was just
|
|
999
|
-
shown addresses the same entry, and so does one that writes it after the parenthesis,
|
|
1000
|
-
`(page.html) #readable`, which {§naked-pattern} reads as the channel. `raw` is therefore always the path alone; a
|
|
1001
|
-
bare path never contains a literal `#`, and `PlurnkParser.stringify` renders the channel back.
|
|
1002
|
-
Without a `#` the field is absent.
|
|
1003
|
-
|
|
1004
|
-
§read-exact-target READ targets one exact resource (a local path or scheme
|
|
1005
|
-
URL, with optional `#channel` fragment or `[metadata]`) and has no body. A
|
|
1006
|
-
`<scope>` on READ selects
|
|
1007
|
-
a text region from that exact target. Without a scope, READ takes the configured
|
|
1008
|
-
first page ({§markerless-first-page}); `<1,-1>` explicitly selects all text. Decimal scope components are
|
|
1009
|
-
invalid on READ.
|
|
1010
|
-
|
|
1011
|
-
Mutation semantics:
|
|
1012
|
-
|
|
1013
|
-
- No scope and a target address that does not yet exist creates a file or entry from the body. This is the only unscoped EDIT.
|
|
1014
|
-
- §unscoped-edit-create-only No scope and an existing target is refused. Replacing existing content requires a precise text scope or `<1,-1>`; core owns the existence check.
|
|
1015
|
-
- `<N>` replaces whole line `N`; `<N,M>` replaces inclusive whole lines `N` through `M`.
|
|
1016
|
-
- An empty body deletes the selected text.
|
|
1017
|
-
- `<0>` prepends and `<-1>` appends.
|
|
1018
|
-
- §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.
|
|
1019
|
-
- `<SL,SC,EL,EC>` deletes the exact exclusive-end region and inserts the body at its start.
|
|
1020
|
-
- §transfer-resource-selections COPY and MOVE require two singular
|
|
1021
|
-
`ResourceSelection` operands, source first and destination second, and admit
|
|
1022
|
-
no body: text after the slots on the heading line, or beneath it, is ignored and the
|
|
1023
|
-
operation still runs, with one warning-severity advisory (`MOVE takes no body; the body
|
|
1024
|
-
was ignored.`). Each selection binds its own target, scope, matcher, and metadata under
|
|
1025
|
-
{§slot-order}, {§matcher-option}, and {§scheme-metadata-modifier}.
|
|
1026
|
-
|
|
1027
|
-
### Per-operation observations
|
|
1028
|
-
|
|
1029
|
-
| OP | Successful observation |
|
|
1030
|
-
|------|-----------------------------------------------------------------------------------|
|
|
1031
|
-
| FIND | Resource catalog groups or exact-target match locations |
|
|
1032
|
-
| READ | Complete or scoped body projections plus optional text match evidence |
|
|
1033
|
-
| EDIT | Status plus a bounded receipt for the effect that landed |
|
|
1034
|
-
| COPY | Source and destination selections plus ordered destination effects |
|
|
1035
|
-
| MOVE | Source and destination selections plus ordered destination and source effects |
|
|
1036
|
-
| SEND | Status and recipient acknowledgement when applicable |
|
|
1037
|
-
| execution | Spawn acknowledgement; output arrives through named stream channels |
|
|
1038
|
-
| BARE | The one-shot model response |
|
|
1039
|
-
| WORK | Spawn acknowledgement; the deliverable arrives through the log |
|
|
1040
|
-
| FORK | Spawn acknowledgement; the inherited worker's deliverable arrives through the log |
|
|
1041
|
-
| KILL | Status of deletion or termination |
|
|
1042
|
-
| NOTE / WAIT | Literal memory or wait explanation |
|
|
1043
|
-
|
|
1044
|
-
§find-result-unit For FIND, target selection fixes the paginated result
|
|
1045
|
-
unit after scheme address resolution ({§entry-address-resolution}). An exact
|
|
1046
|
-
resource target with a matcher pages flat match locations; a glob or
|
|
1047
|
-
folder target, and every matcher-less FIND, pages resources. Resolving a glob to
|
|
1048
|
-
one resource does not make it exact. The same `<N>`, inclusive `<N,M>`,
|
|
1049
|
-
configured markerless first page ({§markerless-first-page}), and explicit-all
|
|
1050
|
-
`<1,-1>` forms apply to either unit.
|
|
1051
|
-
|
|
1052
|
-
§copy-move-observation COPY and MOVE log projections preserve both admitted operand selections,
|
|
1053
|
-
including their independent scopes, whether the result changed state, was a
|
|
1054
|
-
304 no-op, or failed after admission. Operands identify the request; `effects`
|
|
1055
|
-
describe only mutations that landed. If either operand uses textual scope,
|
|
1056
|
-
each landed textual create or update carries the same bounded receipt used by
|
|
1057
|
-
EDIT. Whole-channel transfers remain bodyless structural effects; runtime
|
|
1058
|
-
owners reject binary markers rather than treating a text field as a byte lane.
|
|
112
|
+
| Complete vs. repaired readings | Complete nested interpretations take precedence over missing-closer repairs; among repaired readings, preserving authored operations outranks repair count. |
|
|
113
|
+
| Unclosed mutation yields | An `EDIT` body never swallows a same-width native operation heading; a missing closer is supplied before the next native heading so both operations run. |
|
|
114
|
+
| Prose code blocks and message run-on | Inside `SEND`, `WORK`, `FORK`, or `BARE` bodies, executor headings (` ```sh `, ` ```python3 `) are literal code blocks shown in the message, and an unclosed message body runs to the end of the turn. |
|
|
115
|
+
| Quotations and indented fences | A bare fence or non-operation info string opens an inert quotation block; fences indented by 4+ spaces or a tab are literal CommonMark indented code. |
|
|
116
|
+
| Near-miss tolerances | Inline chains on a closer line, closing fences with stray non-heading trailing text (repaired with trailing characters and any immediately following retry closers discarded), transparent mid-heading closers, case-insensitive executor names, `js`/`ts`/`node` alias fallback, forgotten tag on the line below a bare fence, unclosed header asides, and `@<1-4 digits>` line anchors are recovered with deterministic advisories. |
|
|
117
|
+
| Native tool-call recovery | When a model emits provider-native tool-call markup (`<tool_call>`, `<function=...>`, JSON tool calls) instead of fenced operations, the parser recovers recognizable PLURNK operations or emits a warning receipt for unexecuted markup. |
|
|
118
|
+
| Statement rendering | `PlurnkParser.stringify` formats AST statements back into canonical three-backtick blocks (or wider when the body contains fences), normalizing empty optional bodies to `null`. |
|
|
1059
119
|
|
|
1060
|
-
|
|
1061
|
-
{§operation-result}. Its `status` belongs to the result envelope; a lifecycle operation supplies
|
|
1062
|
-
the authored lifecycle intent. Durable operation observations are projected into a later packet;
|
|
1063
|
-
retrieval never returns inline within the emitting turn.
|
|
120
|
+
### §slot-order Heading slot order, target groups, and metadata modifiers
|
|
1064
121
|
|
|
1065
|
-
|
|
122
|
+
Producers write `(target) <scope> [metadata] <!-- aside -->` separated by single ASCII spaces; ANTLR tolerates adjacent slots and scope/metadata permutations within a resource selection.
|
|
1066
123
|
|
|
1067
|
-
|
|
1068
|
-
| Tag | Promise |
|
|
124
|
+
| Construct | Contract |
|
|
1069
125
|
| --- | --- |
|
|
1070
|
-
|
|
|
1071
|
-
|
|
|
1072
|
-
|
|
|
1073
|
-
|
|
|
1074
|
-
|
|
126
|
+
| Target groups and multi-target slots | A `READ` or `KILL` heading may carry multiple `(path)` slots, and a `FIND`, `READ`, or `KILL` `(path)` slot may carry a comma/pipe/whitespace-separated list of explicit `scheme://` URIs or a comma/pipe-separated list of local paths/globs outside brackets/braces, compiling into one statement per target in authored order. |
|
|
127
|
+
| Scope on scopeless OPs | A `<scope>` written on `WORK`, `FORK`, `BARE`, `NOTE`, or targetless `SEND` is dropped with a warning advisory while the operation still runs. |
|
|
128
|
+
| Heading inline body & bare options | Trailing text on the heading line is read as the matcher on `FIND`/`READ`/`KILL` (and sigil-led `EDIT`), as `[{...}]` metadata when a bare JSON object appears on `SEND`/`WORK`/`FORK`/`BARE`, or as the first body line with a warning advisory. |
|
|
129
|
+
| Scheme metadata modifier | `[metadata]` preserves balanced brackets and quoted strings as raw inner text for the target scheme or executor (`MetadataOptions` parses first-party arrays of option objects and reserves `env`). |
|
|
130
|
+
| Operation aside | A trailing `<!-- aside -->` comment on the heading line is stripped into `aside` (`string` or `null`) as descriptive metadata. |
|
|
1075
131
|
|
|
1076
|
-
|
|
1077
|
-
and path globs share the slot; content matchers follow the heading forms in
|
|
1078
|
-
{§matcher-option}.
|
|
132
|
+
---
|
|
1079
133
|
|
|
1080
|
-
|
|
1081
|
-
|-------------------------|---------------------------------------------------------------------|------------------------------------------------------|
|
|
1082
|
-
| Bare path | `LocalPath { kind: "local", raw, fragment? }` | Resolves through the runtime's file surface; `#channel` is `fragment` ({§local-path-fragment}) |
|
|
1083
|
-
| `scheme://…` | WHATWG-decomposed `UrlPath` | Resolves only when a runtime scheme owns the address |
|
|
1084
|
-
| Path glob | Preserved in either path kind | Scheme defines collection selection and ordering |
|
|
1085
|
-
| `#channel` fragment | Preserved as `UrlPath.fragment` | Selects a named channel when the scheme supports it |
|
|
1086
|
-
| `?query` | Preserved as ordered `UrlPath.query`; `null` = absent, `""` = `?` | Participates in scheme resource identity |
|
|
134
|
+
## §op-shapes 3. Per-operation semantics and dispositions
|
|
1087
135
|
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
136
|
+
| OP | `(path)` | `<scope>` | `body` |
|
|
137
|
+
| --- | --- | --- | --- |
|
|
138
|
+
| `FIND` | required target or glob | optional result range | annotation normalized to `NOTE` |
|
|
139
|
+
| `READ` | required target (exact or glob fan-out) | optional text region | annotation normalized to `NOTE` |
|
|
140
|
+
| `EDIT` | required file or entry | required for existing target; omitted only for creation | literal replacement/creation text |
|
|
141
|
+
| `COPY` / `MOVE` | required source and destination | optional region after each path | empty (any body is ignored with a warning) |
|
|
142
|
+
| execution | fence name is runtime; optional `(program-or-tool)` | none (lifetime in `[metadata]`) | optional program/tool input |
|
|
143
|
+
| `BARE` | optional prompt resource | none | prompt text |
|
|
144
|
+
| `WORK` / `FORK` | optional `session://name` or prompt resource | none | task prompt |
|
|
145
|
+
| `KILL` | required target | optional text region | distillation `NOTE` on `log:///`; empty otherwise |
|
|
146
|
+
| `SEND` | optional recipient | recipient-defined; none for sessions | message body |
|
|
147
|
+
| `NOTE` | none | none | literal working memory |
|
|
148
|
+
| `WAIT` | optional label | none (`[seconds]` in metadata) | wait explanation |
|
|
1093
149
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
URI identity with a grouped object projection.
|
|
150
|
+
- **Retrieval annotations and reasoning operations**: Nonblank bodies beneath `FIND` or `READ` normalize into a preceding `NOTE` statement. `PlurnkParser.parseReasoningOperations` extracts complete line-leading `NOTE`, `FIND`, and `READ` blocks from exposed model reasoning to run before the content program.
|
|
151
|
+
- **Text scope algebra**: `<N>` selects whole line `N` (`0` prepends, `-1` appends on mutation); `<N,M>` selects inclusive lines `N..M`; `<SL,SC,EL,EC>` selects an exact 1-based Unicode code-point region with exclusive end (three-integer scopes normalize to four).
|
|
152
|
+
- **Local path fragments**: Bare paths decompose `#channel` into `fragment` just like `scheme://` URLs so channel selectors round-trip identically.
|
|
153
|
+
- **Turn dispositions (`WAIT` and `SEND`)**: Dispositions may appear anywhere in a turn; multiple `WAIT` statements coalesce to the shortest `[seconds]` bound, and `SEND [200]` (or `[202]` park) governs loop completion once all earlier operations in the turn settle.
|
|
1099
154
|
|
|
1100
|
-
|
|
1101
|
-
tolerant lexer preserves balanced unescaped parentheses, while canonical
|
|
1102
|
-
producers use one lossless Plurnk lexical layer before target interpretation:
|
|
155
|
+
---
|
|
1103
156
|
|
|
1104
|
-
|
|
1105
|
-
| -------------------- | --------------------- |
|
|
1106
|
-
| `\\` | `\` |
|
|
1107
|
-
| `\(` | `(` |
|
|
1108
|
-
| `\)` | `)` |
|
|
157
|
+
## §path-syntax 4. Target and path grammar
|
|
1109
158
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
Pathname producers retain the deliberate `%28`/`%29` alias and `%3C` spelling,
|
|
1114
|
-
but must use the lexical layer for identity-bearing query and fragment text
|
|
1115
|
-
rather than changing their percent-encoded spelling. Newlines and raw `<` are
|
|
1116
|
-
never target content. Glob metacharacters remain legal path data.
|
|
159
|
+
- **URL vs. local path decomposition**: Targets matching `[a-z][a-z0-9+._-]*://` decompose via WHATWG `URL` into `UrlPath` (preserving lossless serialized `query` and `fragment`); all other targets become `LocalPath { kind: "local", raw, fragment? }`.
|
|
160
|
+
- **Parenthesis escaping**: An unescaped depth-zero `)` closes `(path)`; balanced parentheses are tolerated and `\\`, `\(`, `\)` provide lossless escaping via `PathSyntax`.
|
|
161
|
+
- **Glob classification and session names**: `PathSyntax.hasGlob` and `globMagicIndex` classify glob metacharacters vs. exact paths. `SESSION_NAME` (`[A-Za-z0-9][A-Za-z0-9_-]{0,62}`) validates minted session authority names (excluding `_plurnk`).
|
|
1117
162
|
|
|
1118
|
-
|
|
163
|
+
---
|
|
1119
164
|
|
|
1120
|
-
|
|
1121
|
-
| -------------------------------- | ------------------------------------------------------------------------ |
|
|
1122
|
-
| `hasGlob(pathname)` | Recognizes `*`, `?`, character-class, brace, extglob, and escape syntax |
|
|
1123
|
-
| `globMagicIndex(pathname)` | First such position, solely for conservative candidate-prefix selection |
|
|
165
|
+
## §matcher-prefix-claims 5. Bulk pattern matching and scope markers
|
|
1124
166
|
|
|
1125
|
-
|
|
167
|
+
`FIND`, `READ`, `KILL`, and `EDIT` accept a single-line matcher on the heading line classified by leading sigil:
|
|
1126
168
|
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
`
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
## §matcher-prefix-claims 6. Bulk pattern matching
|
|
1137
|
-
|
|
1138
|
-
<!-- map -->
|
|
1139
|
-
| Tag | Promise |
|
|
1140
|
-
| --- | --- |
|
|
1141
|
-
| {§heading-boundary-recovery} | A column-0 heading is the trustworthy boundary. |
|
|
1142
|
-
| {§scope-slot-tolerance} | A line scope written inside a path slot (```` ```COPY (worker:///src.md<2,3>) ````) is read as… |
|
|
1143
|
-
| {§extra-path-slot} | A path slot beyond the operation's admitted operands — one for every operation, two for COPY and MOVE, any… |
|
|
1144
|
-
| {§matcher-refusal} | A matcher the parser cannot read refuses its operation, never the turn. |
|
|
1145
|
-
| {§regex-dialect-readings} | Three habits of other regex dialects are read, with one advisory each. |
|
|
1146
|
-
| {§pattern-body-single-line} | Every matcher is one physical line. |
|
|
1147
|
-
<!-- /map -->
|
|
1148
|
-
|
|
1149
|
-
FIND, READ, KILL, EDIT, and the COPY/MOVE operands accept an optional matcher
|
|
1150
|
-
through the `pattern` option ({§matcher-option}). AstBuilder assigns the dialect from the
|
|
1151
|
-
matcher's leading characters. A leading prefix claims its dialect. Invalid
|
|
1152
|
-
claimed syntax never falls back to glob matching: it is the operation's own
|
|
1153
|
-
refusal ({§matcher-refusal}), not a parse error.
|
|
1154
|
-
|
|
1155
|
-
- §heading-boundary-recovery A column-0 heading is the trustworthy boundary. After a
|
|
1156
|
-
statement-level error the parser discards the rest of that statement and resumes at the
|
|
1157
|
-
next heading; the turn shape is decided locally (a turn disposition is recognized by its own
|
|
1158
|
-
token, never by a whole-turn alternative), so one malformed heading costs one
|
|
1159
|
-
diagnostic and every later statement, the turn disposition included, stands on its own.
|
|
1160
|
-
- §scope-slot-tolerance A line scope written inside a path slot (```` ```COPY (worker:///src.md<2,3>) ````)
|
|
1161
|
-
is read as `(worker:///src.md) <2,3>` — `<` and `>` are not URI characters, so a `<…>` right
|
|
1162
|
-
before a slot's closing paren can only be a scope; every path slot of a statement is repaired
|
|
1163
|
-
the same way — and the slip is one warning-severity advisory at the `<`, placed right after its
|
|
1164
|
-
statement, stating the `(path) <scope>` form that was used. The statement runs; a warning is
|
|
1165
|
-
never a strike. A `<` anywhere else in the slot remains the lexer's refusal.
|
|
1166
|
-
- §extra-path-slot A path slot beyond the operation's admitted operands — one for every
|
|
1167
|
-
operation, two for COPY and MOVE, any number on READ and KILL ({§target-group}) — is a parser
|
|
1168
|
-
error at its opening paren. Report the unexpected slot and the grammar's expected
|
|
1169
|
-
alternatives when available, without inferring pattern intent or imposing another
|
|
1170
|
-
operation's operand count. Recovery-only body text is not advertised as a valid body
|
|
1171
|
-
for an operation that takes none. The statement is dropped and its siblings run.
|
|
169
|
+
| Prefix | Dialect | Validation & tolerance |
|
|
170
|
+
| --- | --- | --- |
|
|
171
|
+
| `//` | `xpath` | Validated with XPath 1.0 parser before regex classification |
|
|
172
|
+
| `/` or `^` | `regex` | ECMAScript `RegExp`; tolerates `\|` alternation, trailing `-flags`, and glob-shaped `/*text*/` with advisories |
|
|
173
|
+
| `$` | `jsonpath` | Validated with RFC 9535 `json-p3` |
|
|
174
|
+
| `~` | `fulltext` | Single-line FTS5 query string |
|
|
175
|
+
| `&` | `graph` | `&symbol`, `&<symbol`, or `&>symbol` relation query |
|
|
176
|
+
| none | `glob` | Fallback shell glob or literal substring |
|
|
1172
177
|
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
| `/` | Regex | `/pattern/flags` | ECMAScript `RegExp` construction | Mimetype projection |
|
|
1177
|
-
| `^` | Regex | `^pattern`, no slashes or flags | ECMAScript `RegExp` construction | Mimetype projection |
|
|
1178
|
-
| `$` | JSONPath | RFC 9535 expression | `json-p3` compilation | Mimetype projection |
|
|
1179
|
-
| `~` | Full-text | `~query` | Single-line raw string | SQLite FTS5 index |
|
|
1180
|
-
| `&` | Graph | `&symbol`, `&<symbol`, or `&>symbol` | Exact shape validation | Symbol index |
|
|
1181
|
-
| none | Glob | Shell glob or literal text | Single-line raw string | Mimetype projection |
|
|
178
|
+
- **Soft matcher refusal**: An invalid matcher expression is admitted as `matcher.dialect = "unreadable"` with its diagnostic and working-form `recovery` so only that operation fails `400 grammar/matcher/unreadable-pattern` while sibling statements in the turn still execute.
|
|
179
|
+
- **Heading recovery and slot tolerances**: Statement errors recover at the next column-0 heading; a `<scope>` written inside `(path<L,M>)` is lifted with a warning; extra `(path)` slots beyond the operation's arity fail only that statement.
|
|
180
|
+
- **Line anchors and scope range recovery**: `TextLineMarker` accepts `@<5 Base62 chars>` line anchors in line positions; reversed or zero-based two-number scopes (`<0,N>`, `<M,N>` with `M > N`, `<N,+K>`) normalize with a warning advisory.
|
|
1182
181
|
|
|
1183
|
-
|
|
1184
|
-
splitting respects escapes and character classes; `\/` represents a literal
|
|
1185
|
-
slash. The AST stores regex `pattern` and `flags`, not a compiled object.
|
|
1186
|
-
SQLite validates full-text query expressions at execution. Graph admission validates its direction
|
|
1187
|
-
and non-whitespace symbol before runtime. Every other leading character remains
|
|
1188
|
-
in the fallback glob/literal dialect; `@(...)` is therefore an extglob group and
|
|
1189
|
-
bare `@text` remains literal matcher text. Rendered READ coordinates are
|
|
1190
|
-
structural output rows, not a reserved matcher prefix. FIND scope selects result
|
|
1191
|
-
positions without changing the matcher.
|
|
182
|
+
---
|
|
1192
183
|
|
|
1193
|
-
|
|
1194
|
-
evaluation belongs to the runtime's selected mimetype, FTS5, or symbol
|
|
1195
|
-
implementation. A matcher admission error is local to its statement; later
|
|
1196
|
-
statements remain recoverable when their boundaries are trustworthy.
|
|
184
|
+
## §whitespace-contract 6. Parser architecture, whitespace, and public API
|
|
1197
185
|
|
|
1198
|
-
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
dialect's working form as `recovery` ({§parse-recovery}); the operation lands as its own `400` row,
|
|
1202
|
-
`grammar/matcher/unreadable-pattern`, with that message and recovery, its siblings run, and the
|
|
1203
|
-
rail treats it as soft ({§strike-progress-immunity} in the core SPEC). An unreadable matcher
|
|
1204
|
-
renders back as written ({§statement-rendering}). Only the one-line and string-typed shape of a
|
|
1205
|
-
`pattern` option remains the option block's own admission.
|
|
1206
|
-
- §regex-dialect-readings **Three habits of other regex dialects are read, with one advisory each.**
|
|
1207
|
-
`\|` is alternation, `|`, and a literal pipe is `[|]`; `-flags` after the closing slash are the
|
|
1208
|
-
flags; a `/pattern/` that fails to compile on a leading or doubled `*` or `?` and is otherwise
|
|
1209
|
-
glob-shaped is read as that glob over each line, `*` as `.*` and `?` as `.`, so `/*/` is every
|
|
1210
|
-
line and `/*kernel_pca*/` contains `kernel_pca`. A regex that compiles is never rewritten, and
|
|
1211
|
-
the AST keeps the authored `raw` beside the pattern it was read as.
|
|
186
|
+
- **Body framing and outside text**: Body framing strips only the opening header newline and the single newline before the closing fence. `PlurnkParser.parse` partitions a model turn into ordered `statement`, `error`, and outside `text` items, flagging unfenced operation-like lines at column 0 with `Unfenced \`OP\` ignored.` warnings.
|
|
187
|
+
- **Recorded emissions corpus**: `test/fixtures/recorded-emissions.jsonl` regresses the parser against real model emissions across live and demo runs.
|
|
188
|
+
- **Tier entry points and runtime-neutral root**: `PlurnkParser.parse` (model turn) and `PlurnkParser.parseStatements` (protocol statements) are exported by `@plurnk/plurnk-parser`. The `@plurnk/plurnk-contracts` root is closed (verified by `src/index.test.ts`) and browser/DOM runtime-neutral (`tsconfig.browser.json`), with Node HTTP host types isolated in `@plurnk/plurnk-contracts/port`.
|
|
1212
189
|
|
|
1213
|
-
|
|
1214
|
-
operations it is the heading line's text ({§naked-pattern}) or the `pattern` option's
|
|
1215
|
-
JSON string ({§matcher-option}); AstBuilder rejects a multiline one before dialect
|
|
1216
|
-
classification. A regex that matches a
|
|
1217
|
-
newline uses the two-character `\n` escape. Non-matcher operation bodies remain
|
|
1218
|
-
multiline.
|
|
190
|
+
---
|
|
1219
191
|
|
|
1220
|
-
## §
|
|
192
|
+
## §text-region 7. Runtime-neutral wire contracts, text regions, and entry reads
|
|
1221
193
|
|
|
1222
|
-
|
|
1223
|
-
| Tag | Promise |
|
|
194
|
+
| Contract | Fields & Invariants |
|
|
1224
195
|
| --- | --- |
|
|
1225
|
-
|
|
|
1226
|
-
| {
|
|
1227
|
-
| {
|
|
1228
|
-
<!-- /map -->
|
|
1229
|
-
|
|
1230
|
-
The model-facing slot is `<scope>`; the AST field is
|
|
1231
|
-
`lineMarker`. Numeric scopes preserve ordered components in `LineMarker`;
|
|
1232
|
-
text-coordinate operations use `TextLineMarker`, whose line positions may also
|
|
1233
|
-
carry rendered anchors. The operation owner assigns every component's role.
|
|
1234
|
-
|
|
1235
|
-
The operation column names the canonical AST operation after
|
|
1236
|
-
{§read-find-normalization}.
|
|
1237
|
-
|
|
1238
|
-
| Operation | Canonical components | Meaning |
|
|
1239
|
-
|-----------------------|----------------------------------------|----------------------------------------------------------------------------|
|
|
1240
|
-
| FIND | 0–2 positions | Inclusive resource or exact-target location positions ({§find-result-unit}; markerless first page under {§markerless-first-page}) |
|
|
1241
|
-
| READ | 0/1/2/4 text coordinates | Text projection from one exact selected file, entry, or log item |
|
|
1242
|
-
| EDIT | 0/1/2/4 text coordinates | Text replacement, deletion, prepend, or append |
|
|
1243
|
-
| COPY/MOVE source | 0/1/2/4 text coordinates | Region copied or moved from the selected source |
|
|
1244
|
-
| COPY/MOVE destination | 0/1/2/4 text coordinates after target | Region replaced or insertion point at the destination |
|
|
1245
|
-
| KILL | 0/1/2 text coordinates | Whole target when absent; one physical line or inclusive range when present ({§kill-scope}) |
|
|
1246
|
-
| execution | None | Lifetime uses metadata; observation cadence belongs to the daemon ({§exec-lifetime}) |
|
|
1247
|
-
| WAIT | None | Maximum park duration uses metadata ({§send-wait-scope}) |
|
|
1248
|
-
| Directed SEND | Owner-defined numeric scope | Carried to the addressed owner; worker actors refuse it ({§send-directed-scope}) |
|
|
1249
|
-
|
|
1250
|
-
Text coordinates use the algebra in {§text-scope-semantics}: one integer is a
|
|
1251
|
-
whole line, two integers are an inclusive whole-line range, and four integers
|
|
1252
|
-
are an exact start-inclusive/end-exclusive region. Mutation scopes additionally
|
|
1253
|
-
admit `0` as prepend and `-1` as append. FIND result positions and READ
|
|
1254
|
-
text coordinates do not admit decimal scope components. A log KILL intersects a valid body-relative line
|
|
1255
|
-
scope with each selected body; an absent line is a successful no-op for that
|
|
1256
|
-
body, while unsupported arity is a runtime failure.
|
|
196
|
+
| `TextRegion` | `{ startLine, startColumn, endLine, endColumn }` — 1-based positive safe integers counting Unicode code points (`LF`, `CRLF`, `CR` line separators; `CRLF` is one indivisible separator). `end` is exclusive; equal start and end identify a zero-length insertion point. Validated by `Validator.assertTextRegion`. |
|
|
197
|
+
| `RangeExtent` | `{ unit: "line" \| "result" \| "byte", total, requested: [first, last], returned?: [first, last] }` — `requested` preserves the numeric request; `returned` names inclusive positive ordered positions (`<= total`) and is omitted on empty or failed selections. Validated by `Validator.assertRangeExtent`. |
|
|
198
|
+
| `EntryReadResult` | `{ status: 200, entry: ClientEntry } \| { status: 400..599, problem: ProblemDetails, entry: null }` — `ClientEntry` carries `{ entryId: number \| null, target: string, channels: Record<string, ClientEntryChannel> }`. Every returned channel (`content`, `contentOffset`, `contentLength`, `mimetype`, `weight`, `state: "static" \| "active" \| "closed" \| "errored"`) satisfies `contentOffset + codePointLength(content) === contentLength`. |
|
|
1257
199
|
|
|
1258
|
-
§
|
|
1259
|
-
spelled `@` followed by exactly five Base62 characters (`0-9A-Za-z`) wherever
|
|
1260
|
-
its `L`, `SL`, or `EL` position denotes a line. Columns, prepend `0`, and append
|
|
1261
|
-
`-1` remain numeric. Exact READ, EDIT, COPY/MOVE source and destination,
|
|
1262
|
-
and KILL preserve these positions in `TextLineMarker`; core resolves them
|
|
1263
|
-
against the addressed current text before operation-specific numeric scope
|
|
1264
|
-
semantics run. FIND's result positions remain numeric and reject anchors. Numeric text scopes remain
|
|
1265
|
-
canonical and fully supported. Parser acceptance does not imply model-facing
|
|
1266
|
-
recommendation.
|
|
1267
|
-
|
|
1268
|
-
§scope-marker-forms Canonical producers separate components with commas and no
|
|
1269
|
-
spaces. ANTLR tolerates a dash separator and one space after a comma. Each
|
|
1270
|
-
component greedily consumes an optional leading minus sign, digits, and an
|
|
1271
|
-
optional decimal fraction, so the ingester preserves even noncanonical numeric
|
|
1272
|
-
shapes for runtime validation. An anchor-bearing text scope uses commas; ANTLR
|
|
1273
|
-
tolerates one space after each comma.
|
|
1274
|
-
|
|
1275
|
-
§scope-range-recovery For FIND result positions and text-coordinate operations,
|
|
1276
|
-
the parser normalizes these two-component numeric scopes, emitting one warning
|
|
1277
|
-
`Scope <written> was read as <canonical>.` per normalized scope:
|
|
1278
|
-
|
|
1279
|
-
| Written coordinates | Canonical inclusive range | Example |
|
|
1280
|
-
|---|---|---|
|
|
1281
|
-
| Positive integers `start,count`, with `count < start` | `start,start+count-1` | `<395,12>` → `<395,406>` |
|
|
1282
|
-
| Positive integer `start` followed by `+offset`, with nonnegative integer offset | `start,start+offset` | `<395,+12>` → `<395,407>` |
|
|
1283
|
-
|
|
1284
|
-
Recovery requires safe integer operands and a safe integer endpoint; arithmetic
|
|
1285
|
-
overflow is refused. Forward/equal ranges, zero and negative sentinels, noninteger
|
|
1286
|
-
coordinates, three/four-component regions, and scopes containing anchors are not
|
|
1287
|
-
reinterpreted as counts. Anchor-relative endpoints retain {§anchor-offset};
|
|
1288
|
-
standalone `+N` has no base and is invalid. WAIT durations and owner-defined SEND
|
|
1289
|
-
or execution scopes are outside this normalization. The AST carries canonical
|
|
1290
|
-
coordinates; the retained emission preserves the authored spelling. These
|
|
1291
|
-
tolerances do not add hot-path teaching.
|
|
1292
|
-
|
|
1293
|
-
After syntax normalization, ordinary {§text-scope-semantics}, pagination and
|
|
1294
|
-
{§read-zero-start} rules apply. Unsupported arity, unresolved inversions and
|
|
1295
|
-
noninteger coordinates remain failures; normalization does not bypass an
|
|
1296
|
-
operation's bounds checks, end clamping or empty-page semantics. FIND owns a
|
|
1297
|
-
deterministic result order so the same inclusive range selects the same positions
|
|
1298
|
-
from unchanged state. The parser does not resolve resource bounds or ordering.
|
|
1299
|
-
|
|
1300
|
-
## 8. Literal programs and code blocks
|
|
1301
|
-
|
|
1302
|
-
A producer carrying literal fences uses an outer backtick count absent from
|
|
1303
|
-
standalone fence lines in its body ({§fence-boundary}). The serializer chooses
|
|
1304
|
-
a count greater than every backtick run in the body; parsed AST values carry
|
|
1305
|
-
no framing state.
|
|
1306
|
-
|
|
1307
|
-
`````text
|
|
1308
|
-
````EDIT (README.md) <1,-1>
|
|
1309
|
-
Run the tests:
|
|
200
|
+
### §diagnostic-observation Operation results, Problem Details, and Notices
|
|
1310
201
|
|
|
1311
|
-
|
|
1312
|
-
npm test
|
|
1313
|
-
```
|
|
1314
|
-
````
|
|
1315
|
-
`````
|
|
1316
|
-
|
|
1317
|
-
The inner shell example is EDIT content, not an execution. The same
|
|
1318
|
-
rule protects code examples in SEND, WORK, FORK, BARE and every other body.
|
|
1319
|
-
|
|
1320
|
-
## 9. Turn dispositions
|
|
1321
|
-
|
|
1322
|
-
<!-- map -->
|
|
1323
|
-
| Tag | Promise |
|
|
202
|
+
| Envelope / Method | Contract |
|
|
1324
203
|
| --- | --- |
|
|
1325
|
-
|
|
|
1326
|
-
|
|
|
1327
|
-
|
|
|
1328
|
-
|
|
|
1329
|
-
|
|
|
1330
|
-
<!-- /map -->
|
|
1331
|
-
|
|
1332
|
-
The runtime adjudicates each admitted turn against actual message resolutions,
|
|
1333
|
-
results and live obligations ({§wait-obligation-matrix}). A response with no
|
|
1334
|
-
operation resolves no message and creates no new observation obligation ({§empty-turn}).
|
|
1335
|
-
|
|
1336
|
-
| Intent | Nominal status | Meaning |
|
|
1337
|
-
|---|---|---|
|
|
1338
|
-
| Unobserved messages or results | 102 | Continue silently |
|
|
1339
|
-
| No operations while messages remain unresolved, or fresh operation/parser failure, without WAIT | 102 | Recover before automatic parking |
|
|
1340
|
-
| WAIT with live obligations | 202 | Park with the supplied or configured duration |
|
|
1341
|
-
| Resolved messages, observed results, live work remains | 202 | Join the held work; replies remain delivered |
|
|
1342
|
-
| Resolved messages, observed results, no held work | 200 or 499 | Settle under {§loop-completion}, retaining the aggregate message outcome |
|
|
1343
|
-
| KILL own worker | 499 | Cancel unfinished work in that worker and its descendants |
|
|
1344
|
-
| Runtime or infrastructure failure | 5xx | Not a model-authored task status |
|
|
1345
|
-
|
|
1346
|
-
### The terminal contract (waitpid)
|
|
1347
|
-
|
|
1348
|
-
The model may supply WAIT or complete message obligations with SEND metadata. The host, not the grammar, owns
|
|
1349
|
-
turn boundaries and adjudicates the loop's actual obligations. Asking
|
|
1350
|
-
the human uses the native `question` executor tool, offered only when a person attends the
|
|
1351
|
-
worker's owner ({§owner-interaction-ring}), not a disposition. The shape rules ARE structural:
|
|
204
|
+
| `OperationResult` | Status `100..399` forbids `problem`; status `400..599` requires one RFC 9457 `ProblemDetails` object whose `status` equals the containing operation status. |
|
|
205
|
+
| `ProblemDetails` | Requires `type` (stable absolute URI), `title` (stable summary with no occurrence data), `status`, and `detail` (failed subject, observed fact, and violated constraint); optional `instance`, `stage`, `recovery` (one working-form next action), `retryable`, and factual producer extensions. |
|
|
206
|
+
| `Problems.fromError(error)` | If `error.result` is present, validates the complete `OperationResult` and returns its `problem`; otherwise if `error.problem` is present, validates and returns `ProblemDetails` unchanged; otherwise returns `null`. |
|
|
207
|
+
| `Problems.project(problem, context)` | Produces `ProblemProjection` for model packets: retains `type` and `detail`, retains `stage`/`recovery`/`retryable`/extensions when not duplicated by the enclosing row, and strips `title`, `status`, and `instance`. |
|
|
208
|
+
| `Notice` | Transient, non-terminal observation (`level`, `source`, `kind`, `message`, optional `directive` starting with `YOU MUST` / `YOU MUST NOT` / `YOU MUST ONLY` / `YOU SHOULD` / `YOU SHOULD NOT` for model-output advisories). |
|
|
1352
209
|
|
|
1353
|
-
-
|
|
1354
|
-
A turn admits any number of lifecycle declarations, anywhere among its
|
|
1355
|
-
operations ({§disposition-anywhere}); the runtime executes them last, as one park.
|
|
1356
|
-
- §disposition-anywhere A disposition may sit anywhere in a model turn
|
|
1357
|
-
without imposing a program boundary. `PlurnkParser.parse` admits every
|
|
1358
|
-
operation before and after it in authored order; the runtime defers only the
|
|
1359
|
-
dispositions until the other admitted operations settle
|
|
1360
|
-
({§op-execution-order}). Nothing is dropped and no diagnostic is raised for
|
|
1361
|
-
position. Omission does not synthesize a disposition ({§turn-shape}).
|
|
1362
|
-
- SEND is communication: an optional recipient path and an optional body.
|
|
1363
|
-
- §park-202-only WAIT joins live work: an open stream or a live
|
|
1364
|
-
child. With none, it does not park. Its optional duration bounds parking ({§send-wait-scope});
|
|
1365
|
-
a future message is scheduled through the schedule family.
|
|
1366
|
-
- §lifecycle-only-turn A WAIT-, SEND-, NOTE-, or KILL-only turn is valid.
|
|
1367
|
-
NOTE does not request parking or acknowledge messages.
|
|
1368
|
-
Ordinary repetition, strike and execution limits still apply.
|
|
210
|
+
- **Diagnostic observation**: Every Problem `detail` and `recovery`, parse diagnostic, and `Notice` states what was read, what was done with it, and where, and may show the working form of the construct it read or refused — never guessing author intent or why.
|
|
1369
211
|
|
|
1370
|
-
|
|
1371
|
-
message to that recipient. Completion metadata resolves message obligations
|
|
1372
|
-
({§message-completion}); dispatch still executes the whole program.
|
|
212
|
+
### §mcp-server-definition Client display capabilities, MCP servers, and skills
|
|
1373
213
|
|
|
1374
|
-
|
|
214
|
+
- **`ClientDisplayCapabilities`**: Ordered array of closed discriminated objects (`{ kind: "scheme", scheme, display }` or `{ kind: "mimetype", mimetype, display }`), where `display.glyph` is an optional non-empty opaque string.
|
|
215
|
+
- **`McpServerDefinition` and `McpOAuth`**: Closed union of `stdio` (`command`, optional `args`, `env`, `cwd`, `plurnkWorkspace`, `plurnkSession`) and `streamable-http` (`url`, optional `headers`, `authorization`, `plurnkWorkspace`, `plurnkSession`) definitions. `McpOAuth` (`Validator.assertMcpOAuth`) validates interactive OAuth (`callbackUrl`, mutually exclusive HTTPS Client ID Metadata Document URL / pre-registered client ID + `${NAME}` secret / Dynamic Client Registration, optional `issuer` and `authorizationParams`) and `client_credentials` grants.
|
|
216
|
+
- **`SkillDefinition`**: Workspace skill definition (optional `source`, `ref`, `commit`; skill alias validated against `SKILL_NAME`) validated by `Validator.assertSkillDefinition`.
|
|
1375
217
|
|
|
1376
|
-
|
|
1377
|
-
also exposes a best-effort `json` value when `JSON.parse` succeeds; invalid JSON
|
|
1378
|
-
leaves `json: null` without invalidating an otherwise legal SEND. Plain text and
|
|
1379
|
-
JSON are both messages, not implicitly stored resources, and the language
|
|
1380
|
-
defines no synthetic scheme or READ-back convention for them.
|
|
218
|
+
### §application-session-observation Exterior application port and session/loop observation
|
|
1381
219
|
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
The implementation this section describes lives in `@plurnk/plurnk-parser`
|
|
1385
|
-
({§parser-consumers}); this section remains the contract it implements.
|
|
1386
|
-
|
|
1387
|
-
ANTLR owns framing, slots and statement composition; AstBuilder produces the
|
|
1388
|
-
schema-owned AST. Where each block ends is decided first, over the whole input, by
|
|
1389
|
-
{§fence-pairing}; the lexer's fence predicates consult that decision and never decide it. Registration, effects and authority remain runtime concerns.
|
|
1390
|
-
|
|
1391
|
-
```mermaid
|
|
1392
|
-
stateDiagram-v2
|
|
1393
|
-
[*] --> DEFAULT
|
|
1394
|
-
DEFAULT --> QUOTATION: a fence that opens no operation
|
|
1395
|
-
QUOTATION --> DEFAULT: its matching closer
|
|
1396
|
-
DEFAULT --> SLOTS: fenced native OP or executor
|
|
1397
|
-
SLOTS --> TARGET: (
|
|
1398
|
-
TARGET --> SLOTS: )
|
|
1399
|
-
SLOTS --> METADATA: [
|
|
1400
|
-
METADATA --> SLOTS: ]
|
|
1401
|
-
SLOTS --> BODY: header newline or tolerated inline body
|
|
1402
|
-
SLOTS --> DEFAULT: matching compact closer
|
|
1403
|
-
BODY --> DEFAULT: matching standalone closer, no nested block
|
|
1404
|
-
BODY --> BODY: nested literal block or other body content
|
|
1405
|
-
```
|
|
1406
|
-
|
|
1407
|
-
## §whitespace-contract 11. Whitespace and interstatement text
|
|
1408
|
-
|
|
1409
|
-
<!-- map -->
|
|
1410
|
-
| Tag | Promise |
|
|
220
|
+
| Projection / Helper | Contract |
|
|
1411
221
|
| --- | --- |
|
|
1412
|
-
|
|
|
1413
|
-
|
|
|
1414
|
-
|
|
|
1415
|
-
|
|
222
|
+
| `ApplicationPort` | Transport-neutral interface in `@plurnk/plurnk-contracts/port` driving workspaces, sessions, loops (`runLoop` with causal `source`, `attachments`, `envelope`), operations, proposals, client interactions, and event subscriptions (`ApplicationOperationEvent`). |
|
|
223
|
+
| `selectSessionLoop` | Chooses `running` before `parked` before `queued` (oldest unresolved sequence wins ties); with no live work, the latest terminal settlement wins. Maintenance-only loops do not change this projection. |
|
|
224
|
+
| `lifecycleOfLoopStatus` | Maps loop status to client lifecycle vocabulary: no loop `idle`; `100` `queued`; `102` `running`; `202` `parked`; `< 400` `completed`; `499` `cancelled`; `>= 400` `failed` (`413` budget, `429` turn ceiling, `500` fail, `504` timeout, `508` runaway). |
|
|
225
|
+
| `excerptOf` | Extracts the first non-blank line of a message truncated to at most `N` code points without splitting a character, with trailing whitespace trimmed and `continued: boolean`. |
|
|
226
|
+
| `ApplicationSessionOverview` | Projects non-hidden model-origin sessions (`{ workspaceId, id, name, parentSessionId, kind, lifecycle, gates, reply }`) ordered by most recently finished reply row first, then newest unreplied sessions, always including all gated sessions before applying `limit` and reporting `omitted`. |
|
|
1416
227
|
|
|
1417
|
-
|
|
1418
|
-
immediately before the closing fence. Every other body character is preserved,
|
|
1419
|
-
including leading/trailing blank lines, indentation, CRLF and literal
|
|
1420
|
-
backslash escapes. A formatter adds its own framing newline even when a body
|
|
1421
|
-
already ends in one. Interstatement whitespace belongs to no body.
|
|
228
|
+
---
|
|
1422
229
|
|
|
1423
|
-
|
|
1424
|
-
Only a header aside has aside semantics. A matching closer ends its body, and no
|
|
1425
|
-
missing closer is inferred. No generic Markdown rendering, indentation stripping
|
|
1426
|
-
or recursive code-block extraction occurs. Exact source remains in `ops://<worker>/`
|
|
1427
|
-
under {§turn-ops-log-curation}; body bytes and source positions are unchanged.
|
|
230
|
+
## §parse-recovery 8. Parse diagnostics, tolerances, and error shapes
|
|
1428
231
|
|
|
1429
|
-
|
|
1430
|
-
The model-turn entry point returns each non-whitespace text span outside operation
|
|
1431
|
-
regions as an ordered `text` item with its exact content and source position.
|
|
1432
|
-
Quoted blocks remain literal text, including every nested operation-looking line.
|
|
1433
|
-
Operation bodies, asides, malformed operation regions and unfenced operation lines
|
|
1434
|
-
({§unfenced-operation}) are not response text;
|
|
1435
|
-
nothing at or beyond a lost boundary is recovered as text. The statement and client
|
|
1436
|
-
tiers ignore outside text. Core alone owns storing it as the turn's outside source ({§outside-text})
|
|
1437
|
-
and no-operation strikes ({§empty-turn}); parsing never infers delivery or completion
|
|
1438
|
-
intent.
|
|
232
|
+
`PlurnkParseError` is a JSON-serializable `Error` carrying `{ line, column, source: "lexer" | "parser" | "visitor", severity: "error" | "warning", recovery?, attempt?, operationOmitted }`:
|
|
1439
233
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
complete aside opens the operation instead ({§naked-operation}). A registered executor's name
|
|
1447
|
-
followed by an operand slot — `gitea (list_issues)`, `sh(build.sh)` — draws the same warning under
|
|
1448
|
-
the executor's own spelling. Quoted blocks, offset lines and names inside a sentence draw nothing.
|
|
234
|
+
| Representation | Line | Column | Absence or extent |
|
|
235
|
+
| --- | --- | --- | --- |
|
|
236
|
+
| Parser/AST `Position` | 1-based | 0-based Unicode code points | `UNKNOWN_POSITION = { line: 0, column: 0 }` is the sole unknown sentinel |
|
|
237
|
+
| Notice `content-offset` | 1-based | 0-based Unicode code points | Omit or set `position` to `null` when unknown |
|
|
238
|
+
| Contracts `TextRegion` | 1-based | 1-based Unicode code points | Start included, end excluded; equality is zero-width |
|
|
239
|
+
| SARIF 2.1.0 region | 1-based | 1-based (`columnKind: "unicodeCodePoints"`) | End excluded; parser point adapters add 1 to `column` |
|
|
1449
240
|
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
| Fact | Rule |
|
|
241
|
+
| Mechanism | Contract |
|
|
1453
242
|
| --- | --- |
|
|
1454
|
-
|
|
|
1455
|
-
|
|
|
1456
|
-
|
|
|
1457
|
-
|
|
1458
|
-
## 12. Public API
|
|
1459
|
-
|
|
1460
|
-
<!-- map -->
|
|
1461
|
-
| Tag | Promise |
|
|
1462
|
-
| --- | --- |
|
|
1463
|
-
| {§turn-shape} | `PlurnkParser.parse` accepts one model turn. |
|
|
1464
|
-
| {§tier-entrypoints} | Each parser entry point owns one document tier: |
|
|
1465
|
-
| {§runtime-neutral-root} | The package root is runtime-neutral. |
|
|
1466
|
-
| {§root-value-api} | The package-root runtime namespace is closed. |
|
|
1467
|
-
| {§parser-construction-boundary} | Parser construction components are internal rather than alternate consumer entry points: |
|
|
1468
|
-
<!-- /map -->
|
|
1469
|
-
|
|
1470
|
-
The package root is the single JavaScript and TypeScript entry point for the language and wire
|
|
1471
|
-
contracts ({§runtime-neutral-root}). Shared AST and wire types come from generated schemas; the small hand-maintained parser
|
|
1472
|
-
types cover ordered parse items and `PlurnkParseError`, which JSON Schema cannot
|
|
1473
|
-
express. Consumers never receive ANTLR parse-tree or token types.
|
|
1474
|
-
|
|
1475
|
-
§turn-shape `PlurnkParser.parse` accepts one model turn. A turn without any
|
|
1476
|
-
authored operation is reported by one hard diagnostic
|
|
1477
|
-
(`No valid Operation Syntax OPs detected.`), alongside any response text
|
|
1478
|
-
({§response-text}). Core admits this as a recoverable turn under {§empty-turn},
|
|
1479
|
-
silently: the diagnostic is the parser's report to its host, never a notice the
|
|
1480
|
-
model is shown.
|
|
1481
|
-
An explicit disposition may sit anywhere in it
|
|
1482
|
-
({§disposition-anywhere}). An omitted WAIT produces no synthesized statement, diagnostic, receipt,
|
|
1483
|
-
warning, or strike. The authored operations and source remain unchanged.
|
|
1484
|
-
Unfinished blocks never receive inferred closers.
|
|
1485
|
-
A bounded operation error leaves its siblings' facts intact, and a lost boundary
|
|
1486
|
-
leaves the facts before it ({§unparsed-tail-boundary}); running them is the
|
|
1487
|
-
consumer's admission ({§emission-admission}).
|
|
1488
|
-
|
|
1489
|
-
The host records programs per turn; no operation acts as a separator between
|
|
1490
|
-
saved programs. There is no outer Markdown program wrapper; the executable
|
|
1491
|
-
blocks themselves are the program.
|
|
1492
|
-
|
|
1493
|
-
§tier-entrypoints Each parser entry point owns one document tier:
|
|
1494
|
-
|
|
1495
|
-
| Entry point | Accepted document | Result statement type |
|
|
1496
|
-
|--------------------------------|----------------------------------------------------------------|-----------------------|
|
|
1497
|
-
| `PlurnkParser.parse` | One model turn, including recoverable outside text and operation diagnostics | `PlurnkStatement` |
|
|
1498
|
-
| `PlurnkParser.parseStatements` | Zero or more protocol statements | `PlurnkStatement` |
|
|
1499
|
-
|
|
1500
|
-
Every entry point returns ordered `statement` and `error` items. The model-turn
|
|
1501
|
-
entry point also returns `text` items under {§response-text}. When present,
|
|
1502
|
-
{§unparsed-tail-boundary} governs the result's item
|
|
1503
|
-
extent. The statement `op` field discriminates the generated per-operation
|
|
1504
|
-
union.
|
|
1505
|
-
|
|
1506
|
-
§runtime-neutral-root **The package root is runtime-neutral.** A browser type-checks it with DOM
|
|
1507
|
-
libraries and no Node globals: nothing it reaches names a Node module or global. The application port
|
|
1508
|
-
({§application-port}), whose HTTP host hands an adapter Node's request and response ({§http-host}), is
|
|
1509
|
-
the one Node-bound contract and its own types-only entry, `@plurnk/plurnk-contracts/port`, with
|
|
1510
|
-
`HttpHost` and `HttpRouteHandler`; the types it speaks stay in the root. A helper that reads a request
|
|
1511
|
-
takes only its headers (`RequestHeaders`), which a Node request satisfies. Lint checks the root's whole
|
|
1512
|
-
source graph under those settings (`tsconfig.browser.json`).
|
|
1513
|
-
|
|
1514
|
-
§root-value-api The package-root runtime namespace is closed. Its exact membership has one home,
|
|
1515
|
-
`src/index.test.ts`, which fails on any addition or removal; the table names the families and
|
|
1516
|
-
their owners. All other root exports are TypeScript types. `PlurnkParser` and `parsePath` are not
|
|
1517
|
-
among them: the parser is `@plurnk/plurnk-parser`'s ({§parser-consumers}).
|
|
1518
|
-
|
|
1519
|
-
| Root value(s) | Consumer contract | Exact owner |
|
|
1520
|
-
|-----------------------------------------------------|---------------------------------------------------------------------|-------------------------------------------|
|
|
1521
|
-
| `Validator` and one `Invalid…Error` per assertion | Validation and typed failure against the owning JSON Schemas | {§wire-entrypoint} |
|
|
1522
|
-
| `Problems` | RFC 9457 Problem construction and model projection | {§problem-details}, {§problem-projection} |
|
|
1523
|
-
| `PlurnkParseError` | JSON-serializable positioned parser diagnostic | {§parse-diagnostics} |
|
|
1524
|
-
| `PathSyntax` | Target-slot spelling and exact-versus-glob classification | {§path-parentheses}, {§path-glob} |
|
|
1525
|
-
| `TurnDisposition` | The one turn disposition and its recognition | {§turn-disposition} |
|
|
1526
|
-
| `CapabilityAdmission` | Admission of a capability descriptor against policy layers | {§capability-admission} |
|
|
1527
|
-
| `PLURNK_OPS`, `isPlurnkOp`, `INTERNAL_ROW_OPS`, `PLURNK_FENCE` | The closed operation alphabet, its membership test, and the language's fence | {§canonical-statement} |
|
|
1528
|
-
| `PROPOSAL_POLICIES` | Server proposal disposition vocabulary | {§proposal-policy} |
|
|
1529
|
-
| `WORKER_NAME` | Authority minting predicate | {§worker-name} |
|
|
1530
|
-
| `UNKNOWN_POSITION` | Frozen sentinel for an AST statement without retained parsed source | {§parser-position} |
|
|
1531
|
-
|
|
1532
|
-
The remaining values are small pure helpers over those contracts (`isExecution`, `writtenOp`,
|
|
1533
|
-
`lifecycleOfLoopStatus`, `selectWorkerLoop`, `renderJsonResult`, `formatJsonDocument`,
|
|
1534
|
-
`aguiConformanceReport`, `requestOrigin` for {§http-request-origin}) and the closed name patterns
|
|
1535
|
-
and vocabularies (`RUNTIME_TAG`, `SKILL_NAME`, `EFFORTS`).
|
|
1536
|
-
|
|
1537
|
-
§parser-construction-boundary Parser construction components are internal rather
|
|
1538
|
-
than alternate consumer entry points:
|
|
1539
|
-
|
|
1540
|
-
| Internal component | Boundary |
|
|
1541
|
-
|--------------------------------------------|---------------------------------------------------------------------------------------------------------------|
|
|
1542
|
-
| `AstBuilder` | Consumes generated ANTLR contexts; `PlurnkParser` and `parsePath` own its API |
|
|
1543
|
-
| `PlurnkErrorStrategy`, `RecordingListener` | Assemble parser recovery and diagnostics around `antlr4ng`; consumers receive `PlurnkParseError` values |
|
|
1544
|
-
|
|
1545
|
-
## 13. Runtime-neutral wire contracts
|
|
1546
|
-
|
|
1547
|
-
<!-- map -->
|
|
1548
|
-
| Tag | Promise |
|
|
1549
|
-
| --- | --- |
|
|
1550
|
-
| {§wire-entrypoint} | The package root exports generated wire types, `Problems`, `Validator` and the value guards `isRecord`,… |
|
|
1551
|
-
| {§text-region} | 13.1 Text regions |
|
|
1552
|
-
| {§range-extent} | 13.2 Range extents |
|
|
1553
|
-
| {§entry-read-result} | 13.3 Client entry reads |
|
|
1554
|
-
| {§operation-result} | Every public PLURNK operation returns one `OperationResult`: |
|
|
1555
|
-
| {§diagnostic-observation} | A diagnostic says what happened, never why. |
|
|
1556
|
-
| {§problem-details} | `ProblemDetails` requires `type`, `title`, `status`, and `detail`; `instance` is optional until a durable… |
|
|
1557
|
-
| {§problem-error-carrier} | `Problems.fromError(error)` recognizes existing failure carriers without depending on a producer's exception… |
|
|
1558
|
-
| {§problem-projection} | `ProblemProjection` is the sole compact model-packet view of an exact `ProblemDetails`. |
|
|
1559
|
-
| {§notice} | A `Notice` is a transient, nonterminal observation. |
|
|
1560
|
-
| {§notice-directive} | A Notice's `message` states what happened ({§diagnostic-observation}). |
|
|
1561
|
-
| {§client-display-capabilities} | 13.7 Client display capabilities |
|
|
1562
|
-
| {§mcp-server-definition} | 13.8 MCP server definitions |
|
|
1563
|
-
| {§mcp-oauth} | `McpOAuth` is the client-managed OAuth plurnk holds for one Streamable HTTP server, included in that server's… |
|
|
1564
|
-
| {§application-port} | 13.9 Exterior application port |
|
|
1565
|
-
| {§application-worker-observation} | Worker observation exposes identity, lineage, kind and lifecycle; it is neither a client binding nor a… |
|
|
1566
|
-
| {§loop-lifecycle-vocabulary} | One projection maps a loop's durable status onto the lifecycle words every client renders, shared by the… |
|
|
1567
|
-
| {§list-excerpt} | A list shows a message by its excerpt. |
|
|
1568
|
-
| {§application-worker-overview} | The worker overview is the places in a scope with what waits there. |
|
|
1569
|
-
| {§application-loop-observation} | Loop observation exposes the durable scheduler state of work loops (excluding maintenance-only administrative… |
|
|
1570
|
-
<!-- /map -->
|
|
1571
|
-
|
|
1572
|
-
§wire-entrypoint The package root exports generated wire types, `Problems`, `Validator` and
|
|
1573
|
-
the value guards `isRecord`, `isSafeInteger` and `errnoCode` alongside the AST types. Their owning
|
|
1574
|
-
JSON Schemas are published through `@plurnk/plurnk-contracts/schema/*.json`, not re-exported
|
|
1575
|
-
as root values.
|
|
1576
|
-
|
|
1577
|
-
### §text-region 13.1 Text regions
|
|
1578
|
-
|
|
1579
|
-
`TextRegion` identifies one contiguous region of textual content:
|
|
1580
|
-
|
|
1581
|
-
| Required field | Coordinate |
|
|
1582
|
-
|----------------|-----------------------------------------------------------|
|
|
1583
|
-
| `startLine` | Line containing the included start |
|
|
1584
|
-
| `startColumn` | Unicode code-point column of the included start |
|
|
1585
|
-
| `endLine` | Line containing the excluded end |
|
|
1586
|
-
| `endColumn` | Unicode code-point column immediately after the selection |
|
|
1587
|
-
|
|
1588
|
-
Lines and columns are positive safe integers and 1-based. Columns count Unicode
|
|
1589
|
-
code points. LF, CRLF, and CR are line separators; CRLF is one indivisible
|
|
1590
|
-
separator, and separator code units are not column positions. The end is
|
|
1591
|
-
exclusive; equal start and end coordinates identify a zero-length insertion
|
|
1592
|
-
point. A producer supplies all four coordinates or omits the region. It never
|
|
1593
|
-
substitutes UTF-16 offsets, readable-row indices, or partial coordinates.
|
|
1594
|
-
`Validator.assertTextRegion` rejects an end before its start.
|
|
1595
|
-
|
|
1596
|
-
### §range-extent 13.2 Range extents
|
|
1597
|
-
|
|
1598
|
-
`RangeExtent` is the compact wire projection of one line or ordered-result
|
|
1599
|
-
selection: `{ unit, total, requested: [first,last], returned?: [first,last] }`.
|
|
1600
|
-
`requested` preserves the numeric request, including invalid fractional
|
|
1601
|
-
evidence on a failed selection; a one-position request therefore repeats its
|
|
1602
|
-
endpoint. Successful selection endpoints are integers. `total` is the complete
|
|
1603
|
-
available cardinality.
|
|
1604
|
-
`returned` names the inclusive positions actually projected and is absent for
|
|
1605
|
-
an empty selection or a failed request. Its endpoints are positive, ordered,
|
|
1606
|
-
and no greater than `total`.
|
|
1607
|
-
|
|
1608
|
-
The transparent coordinates make completion and continuation derivable. The
|
|
1609
|
-
shape has no separate `complete`, `next`, or all-results instruction. Exact
|
|
1610
|
-
text-coordinate selections use {§text-region} instead. `Validator.assertRangeExtent`
|
|
1611
|
-
enforces both the schema and the relational endpoint invariants. `unit: "byte"`
|
|
1612
|
-
names a selection of a binary resource's bytes, positions being 1-based byte
|
|
1613
|
-
offsets; the byte view is one octet per line, so the same positional algebra
|
|
1614
|
-
applies ({§read-bytes} in the core specification).
|
|
1615
|
-
|
|
1616
|
-
### §entry-read-result 13.3 Client entry reads
|
|
1617
|
-
|
|
1618
|
-
`EntryReadResult` is the exact transport-neutral projection of one entry. It
|
|
1619
|
-
does not expose workspace IDs, storage owner IDs, split persistence-coordinate fields,
|
|
1620
|
-
scope, or other persistence columns.
|
|
1621
|
-
|
|
1622
|
-
| Outcome | Exact shape |
|
|
1623
|
-
|---------|------------------------------------------------------------------|
|
|
1624
|
-
| Success | `{ status: 200, entry: ClientEntry }` |
|
|
1625
|
-
| Failure | `{ status: 400..599, problem: ProblemDetails, entry: null }` |
|
|
1626
|
-
|
|
1627
|
-
| `ClientEntry` field | Contract |
|
|
1628
|
-
|---------------------|----------------------------------------------------------------------------------------------|
|
|
1629
|
-
| `entryId` | Positive durable entry identifier, or `null` for a representation core computes rather than stores |
|
|
1630
|
-
| `target` | Client selector for the resolved entry, with any channel fragment removed |
|
|
1631
|
-
| `channels` | Every channel for a full read, or exactly the selected channel for a sliced read, by name; the empty name is the unnamed default channel of a scheme whose default is unnamed, a log row's |
|
|
1632
|
-
|
|
1633
|
-
| Channel field | Contract |
|
|
1634
|
-
|-----------------|---------------------------------------------------------------------------------------------------------------|
|
|
1635
|
-
| `content` | Full content, or the suffix beginning at `contentOffset` |
|
|
1636
|
-
| `contentOffset` | Actual Unicode-code-point offset of `content`; zero for a full read and capped at `contentLength` |
|
|
1637
|
-
| `contentLength` | Unicode-code-point length of the complete stored channel |
|
|
1638
|
-
| `mimetype` | Stored channel mimetype |
|
|
1639
|
-
| `weight` | Stored model-independent curation weight for the complete channel |
|
|
1640
|
-
| `state` | `static`, `active`, `closed`, or `errored` |
|
|
1641
|
-
|
|
1642
|
-
For every returned channel,
|
|
1643
|
-
`contentOffset + codePointLength(content) === contentLength`. Therefore an
|
|
1644
|
-
offset beyond the current end returns empty content at `contentLength`, not the
|
|
1645
|
-
unbounded requested offset. `Validator.assertEntryReadResult` enforces the
|
|
1646
|
-
schema, this suffix invariant, and Problem status equality.
|
|
1647
|
-
|
|
1648
|
-
### 13.4 Operation results
|
|
1649
|
-
|
|
1650
|
-
§operation-result Every public PLURNK operation returns one `OperationResult`:
|
|
1651
|
-
|
|
1652
|
-
| Status | Required shape |
|
|
1653
|
-
|---------|-------------------------------------------|
|
|
1654
|
-
| 100–399 | `problem` is forbidden |
|
|
1655
|
-
| 400–599 | One RFC 9457 `problem` object is required |
|
|
1656
|
-
|
|
1657
|
-
Producer-specific success fields and Problem Details extension members remain
|
|
1658
|
-
open. A malformed result is
|
|
1659
|
-
an internal producer contract violation; it is not converted into a second
|
|
1660
|
-
model-facing failure envelope.
|
|
1661
|
-
|
|
1662
|
-
### 13.5 Diagnostics and Problem Details
|
|
1663
|
-
|
|
1664
|
-
§diagnostic-observation **A diagnostic says what happened, never why.** Every Problem `detail`
|
|
1665
|
-
and `recovery`, parse diagnostic and notice names what was read, what was done with it, and
|
|
1666
|
-
where, and may show the working form of the construct it read or refused. It never names a cause
|
|
1667
|
-
or an intent the input does not establish: no likely mistake, no reconstruction of what the author
|
|
1668
|
-
meant, no alternative the producer did not apply, and no form the language no longer has. Each
|
|
1669
|
-
sentence presupposes only what the producer itself decided.
|
|
1670
|
-
|
|
1671
|
-
§problem-details `ProblemDetails` requires `type`, `title`, `status`, and `detail`;
|
|
1672
|
-
`instance` is optional until a durable host can attach the occurrence URI.
|
|
1673
|
-
|
|
1674
|
-
| Field | Contract |
|
|
1675
|
-
|-------------|-----------------------------------------------------------------------------------------------------------------------------------------|
|
|
1676
|
-
| `type` | Stable absolute URI for the problem class |
|
|
1677
|
-
| `title` | Stable summary with no occurrence data or instruction |
|
|
1678
|
-
| `status` | Equals the containing operation status |
|
|
1679
|
-
| `detail` | Tersely states the failed subject, observed fact, and violated constraint at the layer that knows the cause |
|
|
1680
|
-
| `instance` | Durable URI for this occurrence |
|
|
1681
|
-
| `stage` | Stable failed stage, only when neighboring stages imply different recovery |
|
|
1682
|
-
| `recovery` | One generally valid next action; omitted when the producer cannot know |
|
|
1683
|
-
| `retryable` | `true` only when the producer recommends automatically retrying the identical request; otherwise false or unknown/omitted as applicable |
|
|
1684
|
-
| extensions | Factual producer-known operands or constraints |
|
|
1685
|
-
|
|
1686
|
-
`detail` is failure truth; `recovery` is not a second explanation. Producers do
|
|
1687
|
-
not blame the model, restate status, or expose an implementation accident as the
|
|
1688
|
-
cause ({§diagnostic-observation}). General syntax and workflow teaching remain in the model
|
|
1689
|
-
packet rather than being repeated in every Problem.
|
|
1690
|
-
|
|
1691
|
-
`Problems.create(owner, code, status, detail, extensions?, options?)` derives a
|
|
1692
|
-
stable title from `code` unless an established type supplies
|
|
1693
|
-
`options.title`. Occurrence-specific text never belongs in `title`.
|
|
1694
|
-
|
|
1695
|
-
Internal invariant violations throw and preserve their cause. An external
|
|
1696
|
-
protocol may require its own error envelope; its adapter maps that envelope to
|
|
1697
|
-
or from the canonical Problem without creating another PLURNK failure contract.
|
|
1698
|
-
|
|
1699
|
-
§problem-error-carrier `Problems.fromError(error)` recognizes existing failure
|
|
1700
|
-
carriers without depending on a producer's exception class:
|
|
1701
|
-
|
|
1702
|
-
| Carrier | Interpretation |
|
|
1703
|
-
|---------|----------------|
|
|
1704
|
-
| `error.result` present | Validate the complete {§operation-result}; return its Problem, if any |
|
|
1705
|
-
| Otherwise, `error.problem` present | Validate and return {§problem-details} unchanged |
|
|
1706
|
-
| Absent or malformed carrier | Return `null`; the caller retains the original exception as an unexpected failure |
|
|
1707
|
-
|
|
1708
|
-
Recognition never derives recovery text from an exception message, changes its
|
|
1709
|
-
status, or rescues a malformed result through a second Problem field. Unexpected
|
|
1710
|
-
accessor or validator exceptions propagate.
|
|
1711
|
-
|
|
1712
|
-
§problem-projection `ProblemProjection` is the sole compact model-packet view of
|
|
1713
|
-
an exact `ProblemDetails`. `Problems.project(problem, context)` validates both
|
|
1714
|
-
representations and rejects a status that contradicts the enclosing row.
|
|
1715
|
-
|
|
1716
|
-
| Projection member | Contract |
|
|
1717
|
-
|-------------------|----------|
|
|
1718
|
-
| `type`, `detail` | Always retained; together they identify the stable class and occurrence-specific cause |
|
|
1719
|
-
| `stage`, `recovery`, `retryable`, extensions | Retained only when present and not exactly duplicated by the enclosing row |
|
|
1720
|
-
| `title`, `status`, `instance` | Forbidden; the exact Problem owns stable title and occurrence identity while the enclosing packet row already owns model-facing status and address |
|
|
1721
|
-
|
|
1722
|
-
Projection never mutates or replaces the exact Problem. Durable storage,
|
|
1723
|
-
external protocols, clients, and forensic artifacts continue to receive the
|
|
1724
|
-
complete RFC 9457 object.
|
|
1725
|
-
|
|
1726
|
-
### 13.6 Notices
|
|
1727
|
-
|
|
1728
|
-
§notice A `Notice` is a transient, nonterminal observation. It cannot determine durable
|
|
1729
|
-
failure truth, lifecycle, scheduling, or recovery. Sharing a renderer with
|
|
1730
|
-
Problems does not merge their semantics.
|
|
1731
|
-
|
|
1732
|
-
§notice-directive A Notice's `message` states what happened ({§diagnostic-observation}).
|
|
1733
|
-
When the observation concerns the model's own output, the producer may attach one
|
|
1734
|
-
`directive`: `YOU MUST`, `YOU MUST NOT`, `YOU MUST ONLY`, `YOU SHOULD` or
|
|
1735
|
-
`YOU SHOULD NOT`, then the rule. An observation of the environment carries none.
|
|
1736
|
-
Clients display the message; the model packet leads with the directive.
|
|
1737
|
-
|
|
1738
|
-
### §client-display-capabilities 13.7 Client display capabilities
|
|
1739
|
-
|
|
1740
|
-
`ClientDisplayCapabilities` is the transport-neutral installed-capability
|
|
1741
|
-
projection used by external clients. It is an ordered array of closed,
|
|
1742
|
-
discriminated values:
|
|
1743
|
-
|
|
1744
|
-
| `kind` | Identity field | `display` |
|
|
1745
|
-
|--------|----------------|-----------|
|
|
1746
|
-
| `scheme` | Non-empty `scheme` URI-family name | `CapabilityDisplay` |
|
|
1747
|
-
| `mimetype` | Non-empty `mimetype` media type | `CapabilityDisplay` |
|
|
1748
|
-
|
|
1749
|
-
`CapabilityDisplay` is a closed object whose optional `glyph` is a non-empty,
|
|
1750
|
-
opaque string. Frameworks own and validate their intrinsic
|
|
1751
|
-
declarations; Core owns composition of the installed extensions; interface
|
|
1752
|
-
modules expose this exact shape; clients own rendering, font support, theme,
|
|
1753
|
-
and identity fallback when `glyph` is absent. Empty framework sentinels are
|
|
1754
|
-
normalized to absence at composition. Display metadata is client state, never
|
|
1755
|
-
model-language syntax or model packet teaching.
|
|
1756
|
-
|
|
1757
|
-
### §mcp-server-definition 13.8 MCP server definitions
|
|
1758
|
-
|
|
1759
|
-
`McpServerDefinition` is the one definition the workspace `mcp` Functionality family
|
|
1760
|
-
accepts and persists: one complete connection definition with its alias `name`.
|
|
1761
|
-
It is a closed union of the two supported transports, `stdio`
|
|
1762
|
-
(`command`, optional `args`, `env`, `cwd`) and `streamable-http` (`url`, optional
|
|
1763
|
-
`headers` and `authorization`). Source provenance, workspace scope, connection
|
|
1764
|
-
state, catalog and resolved credentials are not definition fields. Adding or
|
|
1765
|
-
removing a definition does not install or remove a plugin. Transport and
|
|
1766
|
-
authentication fields replace together ({§configuration-definition-resolution}).
|
|
1767
|
-
|
|
1768
|
-
§mcp-oauth `McpOAuth` is the client-managed OAuth plurnk holds for one Streamable
|
|
1769
|
-
HTTP server, included in that server's `authorization`. Interactive OAuth always requires a callback URL, and
|
|
1770
|
-
its structurally exclusive identity modes are an HTTPS Client ID Metadata Document URL,
|
|
1771
|
-
a pre-registered client ID with a symbolic secret, or neither for server-advertised
|
|
1772
|
-
Dynamic Client Registration. Any interactive mode may name the `issuer` its sign-in binds to,
|
|
1773
|
-
where discovery starts, and `authorizationParams` added to the authorization request; the
|
|
1774
|
-
parameters the flow sets itself cannot be named. A client-credentials grant names its client ID and
|
|
1775
|
-
symbolic secret, optionally binding an issuer. Every secret is one complete `${NAME}`
|
|
1776
|
-
reference to the operator environment; `Validator.assertMcpOAuth` validates the shape.
|
|
1777
|
-
|
|
1778
|
-
`SkillDefinition` is the one definition the workspace `skills` Functionality
|
|
1779
|
-
family accepts and persists: the standard `name`, its `source`, an optional git
|
|
1780
|
-
`ref`, and the `commit` the service recorded. Only host-provided trees omit
|
|
1781
|
-
`source`; installation scopes are not part of this shape. `Validator.assertSkillDefinition` validates the wire shape;
|
|
1782
|
-
the schema's name grammar is exposed as `SKILL_NAME` for loaders and discovery
|
|
1783
|
-
({§agent-skills-name}). Core owns source resolution and lifecycle
|
|
1784
|
-
({§skills-functionality}).
|
|
1785
|
-
|
|
1786
|
-
`A2aAgentDefinition` is the one definition the Worker `a2a` Functionality
|
|
1787
|
-
family accepts and persists: the local alias `name` (the `a2a://<name>`
|
|
1788
|
-
authority), the remote `url` whose standard Agent Card remains the protocol
|
|
1789
|
-
authority, and optional local `cardPath`, `headers`, and bearer
|
|
1790
|
-
`authorization` whose token is a symbolic `${NAME}` reference.
|
|
1791
|
-
`Validator.assertA2aAgentDefinition` is the family's admission boundary.
|
|
1792
|
-
|
|
1793
|
-
### §application-port 13.9 Exterior application port
|
|
1794
|
-
|
|
1795
|
-
`ApplicationPort` is the single transport-neutral TypeScript contract through
|
|
1796
|
-
which an exterior adapter drives and observes the Plurnk application. Core
|
|
1797
|
-
implements it; AG-UI, A2A, and other interface modules consume it from
|
|
1798
|
-
`@plurnk/plurnk-contracts/port` ({§runtime-neutral-root}). The port
|
|
1799
|
-
contains typed application calls and a scoped event subscription, not wire
|
|
1800
|
-
route names, protocol framing, persistence access, or adapter-specific methods.
|
|
1801
|
-
An exterior adapter owns its own protocol validation, identity binding, and
|
|
1802
|
-
projection while reusing the same workspace, worker, loop, operation, proposal,
|
|
1803
|
-
interaction, and event owners through this port.
|
|
1804
|
-
|
|
1805
|
-
`ApplicationOperationEvent` is the schema-owned dispatch observation consumed
|
|
1806
|
-
through that subscription; its production boundary is {§notifications-operation-event}.
|
|
1807
|
-
|
|
1808
|
-
`configurationNotices` exposes current launcher-owned configuration diagnostics
|
|
1809
|
-
without acquiring a workspace or provider. Adapters use the existing Notice
|
|
1810
|
-
projection; model turns combine these with workspace diagnostics according to
|
|
1811
|
-
{§configuration-repair-path}.
|
|
1812
|
-
|
|
1813
|
-
`runLoop.source` is trusted causal provenance supplied by an adapter, distinct
|
|
1814
|
-
from user-authored prompt content. An adapter may expose no public means to set
|
|
1815
|
-
it; Core validates and records it through the same prompt admission path.
|
|
1816
|
-
|
|
1817
|
-
`runLoop.attachments` carries typed bytes selected by the adapter;
|
|
1818
|
-
`runLoop.envelope` retains opaque protocol evidence under
|
|
1819
|
-
{§message-envelope-evidence}. `readMessages` projects durable inbox messages
|
|
1820
|
-
and successful replies independently of log visibility, retaining
|
|
1821
|
-
each reply's `answers` addresses (empty for incoming messages).
|
|
1822
|
-
`resolveClientInteraction` may carry the accepted answer's message evidence;
|
|
1823
|
-
Core validates the resolution, retains the arrival, then resumes the operation.
|
|
1824
|
-
|
|
1825
|
-
§application-worker-observation **Worker observation exposes identity, lineage, kind and lifecycle; it is neither a client binding nor a permission grant.** It exposes durable identity, origin, immediate parent identity, minted `kind` (`root` for a worker with no parent; `fork` for a child carrying a fork boundary; `work` for any other child), and `lifecycle`, the worker's representative work loop projected through {§loop-lifecycle-vocabulary} (`idle` when it has none), and `hidden_at`, when the worker was hidden ({§place-hiding}), null while it is shown. A client renders kind and lifecycle, it never infers them.
|
|
1826
|
-
|
|
1827
|
-
| Fact | Rule |
|
|
1828
|
-
| --- | --- |
|
|
1829
|
-
| `selectWorkerLoop` | chooses running before parked before queued; ties select the oldest unresolved sequence; with no live work, the latest terminal settlement wins (sequence breaks equal timestamps); newer terminal history never hides unfinished work; maintenance-only loops do not change this projection, and their ordinary turns and results remain durable history |
|
|
1830
|
-
| `readWorker` | resolves exactly one id or name and returns `null` when absent |
|
|
1831
|
-
| `listWorkers` | filters collections by origin, lineage position or mark; an omitted parent filter means every position and an explicit `null` means roots; an omitted `hidden` means shown and hidden workers alike; singular and plural cardinalities are distinct contracts |
|
|
1832
|
-
|
|
1833
|
-
§loop-lifecycle-vocabulary One projection maps a loop's durable status onto the
|
|
1834
|
-
lifecycle words every client renders, shared by the status gauge and the worker
|
|
1835
|
-
directory: no loop `idle`; 100 `queued`; 102 `running`; 202 `parked`; any other status is a
|
|
1836
|
-
concluded loop's: below 400 `completed`; 499 `cancelled`, a loop stopped without failing
|
|
1837
|
-
({§agui-run-outcome}); any other of 400 or more `failed` (413 budget, 429 turn ceiling, 500 fail,
|
|
1838
|
-
504 execution timeout, 508 runaway). `lifecycleOfLoopStatus`
|
|
1839
|
-
in `@plurnk/plurnk-contracts` is that projection's one owner.
|
|
1840
|
-
|
|
1841
|
-
§list-excerpt **A list shows a message by its excerpt.** The excerpt is the message's first line holding anything but
|
|
1842
|
-
blanks (spaces, tabs and line ends), cut to at most N characters, a character never cut in two, with blanks trimmed from
|
|
1843
|
-
its end; `continued` says whether anything but blanks follows. `excerptOf` in `@plurnk/plurnk-contracts` is that rule's one
|
|
1844
|
-
owner, for a loop's opening in a worker's loop list ({§agui-worker-loops}) and a worker's latest finished reply
|
|
1845
|
-
({§worker-finished-reply}).
|
|
1846
|
-
|
|
1847
|
-
§application-worker-overview **The worker overview is the places in a scope with what waits there.** A place is a
|
|
1848
|
-
model-origin worker; bookkeeping workers are not places. The scope is the daemon or one workspace; hidden places are out
|
|
1849
|
-
of it ({§place-hiding}).
|
|
1850
|
-
|
|
1851
|
-
| Fact | Rule |
|
|
1852
|
-
| --- | --- |
|
|
1853
|
-
| A place | `{ workspaceId, id, name, parentWorkerId, kind, lifecycle, gates, reply }`: `kind` and `lifecycle` as {§application-worker-observation} projects them, `gates` the proposals and client interactions it waits on as `pendingProposals` and `pendingClientInteractions` project them, `reply` its latest finished reply or `null` |
|
|
1854
|
-
| A reply | `{ id, coordinate, completion, at, excerpt, continued }`: its durable log row, `L/T/S` in its worker's log, the completion its receipt records, when it was recorded (Unix milliseconds) and its excerpt ({§list-excerpt}) |
|
|
1855
|
-
| Order | most recently finished first, by reply row (the log's commit order, so two replies never tie), then the places with no reply, newest first |
|
|
1856
|
-
| Bound | every place waiting on a gate, then the first `limit` in that order; `omitted` counts the places in scope it leaves out, so no place is left out silently |
|
|
1857
|
-
| Workspaces | `{ id, name, projectRoot, projectLabel }` for each workspace a held place belongs to ({§workspace-label}) |
|
|
1858
|
-
|
|
1859
|
-
§application-loop-observation Loop observation exposes the durable scheduler
|
|
1860
|
-
state of work loops (excluding maintenance-only administrative loops), nullable
|
|
1861
|
-
`waitUntil` (the durable observation deadline in Unix milliseconds), the times a loop first
|
|
1862
|
-
ran (`claimedAt`, {§loop-claim-latency}) and concluded (`terminatedAt`), ISO 8601 UTC and null
|
|
1863
|
-
until they happen, exact terminal `OperationResult`, the operation that opened the loop
|
|
1864
|
-
(`invocation`: its Worker and log address, {§worker-obligations}; null when no Worker's operation did),
|
|
1865
|
-
and exact count of packet-bearing
|
|
1866
|
-
Turns for one owned Worker. Packetless producer Turns and physical provider
|
|
1867
|
-
retries do not contribute to `packetCount`. Exterior
|
|
1868
|
-
adapters consume this projection instead of reconstructing lifecycle from
|
|
1869
|
-
events or persistence; events remain the live notification edge.
|
|
1870
|
-
|
|
1871
|
-
## 14. Parse diagnostics
|
|
1872
|
-
|
|
1873
|
-
<!-- map -->
|
|
1874
|
-
| Tag | Promise |
|
|
1875
|
-
| --- | --- |
|
|
1876
|
-
| {§parse-diagnostics} | `PlurnkParseError` is a JSON-serializable Error subclass. |
|
|
1877
|
-
| {§parse-admission} | A diagnostic's severity does not describe whether an operation ran. |
|
|
1878
|
-
| {§parse-recovery} | Every hard diagnostic carries its working form. |
|
|
1879
|
-
| {§parser-position} | Parser source locations are points, not text regions. |
|
|
1880
|
-
| {§inline-flag-tolerance} | PCRE inline modifiers. |
|
|
1881
|
-
| {§regex-trailing-text} | A valid `/pattern/flags` prefix followed by horizontal whitespace and trailing text receives one concise… |
|
|
1882
|
-
| {§regex-sed-range} | A sed line range is named as one. |
|
|
1883
|
-
| {§unclosed-regex} | A regex that never closes. |
|
|
1884
|
-
| {§naked-pattern} | The matcher rides the heading bare. |
|
|
1885
|
-
| {§trailing-slots} | Slots after the matcher peel off the right. |
|
|
1886
|
-
| {§log-heading-notation} | The log's heading notation, copied as syntax, is read as the slot it stands for. |
|
|
1887
|
-
| {§bare-target} | A target written without its parentheses is refused with the line that runs. |
|
|
1888
|
-
| {§bare-anchor-scope} | An EDIT's anchor without its angle brackets is its scope. |
|
|
1889
|
-
| {§matcher-body-redirect} | Unread selector text. |
|
|
1890
|
-
| {§combined-anchor-tolerance} | Combined line number and anchor in a scope. |
|
|
1891
|
-
| {§invalid-scope-diagnostic} | Malformed scope content. |
|
|
1892
|
-
| {§error-shape} | The diagnostic class determines the parts of a message; every class says what happened, never why… |
|
|
1893
|
-
| {§parse-attempt} | A hard fact that refused a statement the parser opened carries its `attempt`: the statement's text exactly as… |
|
|
1894
|
-
| {§unparsed-tail-boundary} | When the lexer cannot determine where a malformed statement ends — an unfinished `(target` or `[metadata`… |
|
|
1895
|
-
<!-- /map -->
|
|
1896
|
-
|
|
1897
|
-
§parse-diagnostics `PlurnkParseError` is a JSON-serializable Error subclass.
|
|
1898
|
-
Its `message` contains only the parser-owned diagnostic; position, source, and
|
|
1899
|
-
severity remain separate facts.
|
|
1900
|
-
|
|
1901
|
-
```typescript
|
|
1902
|
-
type ErrorSource = "lexer" | "parser" | "visitor";
|
|
1903
|
-
type Severity = "error" | "warning";
|
|
1904
|
-
|
|
1905
|
-
class PlurnkParseError extends Error {
|
|
1906
|
-
readonly line: number;
|
|
1907
|
-
readonly column: number;
|
|
1908
|
-
readonly source: ErrorSource;
|
|
1909
|
-
readonly severity: Severity;
|
|
1910
|
-
readonly recovery: string | undefined;
|
|
1911
|
-
readonly operationOmitted: boolean;
|
|
1912
|
-
}
|
|
1913
|
-
```
|
|
1914
|
-
|
|
1915
|
-
§parse-admission **A diagnostic's severity does not describe whether an operation ran.**
|
|
1916
|
-
`operationOmitted` identifies unexecuted unfenced operations and unrecovered native
|
|
1917
|
-
calls. A warning about a retained operation or a quoted
|
|
1918
|
-
example does not set it. This fact survives serialization when true; consumers do
|
|
1919
|
-
not infer it from diagnostic wording. It does not independently prescribe a strike.
|
|
1920
|
-
|
|
1921
|
-
§parse-recovery **Every hard diagnostic carries its working form.** A `severity: "error"`
|
|
1922
|
-
diagnostic names, in `recovery`, the form of the construct it refused, in the model's terms: a
|
|
1923
|
-
refused heading carries the operation's canonical line (`` `READ (path) <L,M>? pattern? <!-- aside -->?`
|
|
1924
|
-
on the opening fence line. ``), a refused scope the scope's form, and a refused
|
|
1925
|
-
matcher its dialect's form with an example (`` A pattern is a regex written `/pattern/flags`, such
|
|
1926
|
-
as `/timeout/i`. ``). The working form is the language's, never a reconstruction of the input
|
|
1927
|
-
({§diagnostic-observation}). The runtime projects `recovery` as the Problem's `recovery` beside the
|
|
1928
|
-
verbatim `message`; an advisory carries none. A refused matcher carries
|
|
1929
|
-
the same pair into its own operation's Problem ({§matcher-refusal}).
|
|
1930
|
-
|
|
1931
|
-
§parser-position Parser source locations are points, not text regions. An AST
|
|
1932
|
-
statement's `position` identifies the first backtick of its header; a diagnostic
|
|
1933
|
-
identifies the offending or recovery point; `unparsedTail.from` identifies where
|
|
1934
|
-
the undefined tail begins. A
|
|
1935
|
-
statement constructed without retained parsed source uses `UNKNOWN_POSITION`,
|
|
1936
|
-
the unknown sentinel; its dispatch origin remains a separate fact.
|
|
1937
|
-
|
|
1938
|
-
| Representation | Line | Column | Absence or extent |
|
|
1939
|
-
|-------------------------------------|--------------------------|------------------------------------------|--------------------------------------------------------|
|
|
1940
|
-
| Parser/AST `Position` | 1-based | 0-based Unicode code points | `{ line: 0, column: 0 }` is the sole unknown sentinel |
|
|
1941
|
-
| Notice `content-offset` | 1-based | 0-based Unicode code points | Omit or set `position` to null when unknown |
|
|
1942
|
-
| Contracts `TextRegion` | 1-based | 1-based Unicode code points | Start included, end excluded; equality is zero-width |
|
|
1943
|
-
| SARIF 2.1.0 line/column region | 1-based | 1-based, declared by `columnKind` | End excluded; equality is zero-width |
|
|
1944
|
-
|
|
1945
|
-
Parser columns count code points, not UTF-16 code units or grapheme clusters.
|
|
1946
|
-
LF and CRLF delimit source lines; a lone CR occupies one column. A point may sit
|
|
1947
|
-
immediately after the final code point and carries no implicit character extent.
|
|
1948
|
-
Consumers preserve parser points unchanged inside PLURNK. A SARIF adapter that
|
|
1949
|
-
preserves one must add one to `column`, declare `columnKind` as
|
|
1950
|
-
`"unicodeCodePoints"`, and emit equal start/end coordinates rather than
|
|
1951
|
-
inventing an extent. `TextRegion` already uses SARIF's base and exclusive-end algebra but
|
|
1952
|
-
remains a distinct contracts-owned representation. See [SARIF 2.1.0 §§3.14.26–27
|
|
1953
|
-
and 3.30.2](https://docs.oasis-open.org/sarif/sarif/v2.1.0/sarif-v2.1.0.html).
|
|
1954
|
-
|
|
1955
|
-
| Source | Boundary |
|
|
1956
|
-
|-------------|----------------------------------------------------------------------------------------|
|
|
1957
|
-
| `"lexer"` | Token-level failure, such as an unrecognized character or malformed `<L>` integer. |
|
|
1958
|
-
| `"parser"` | Structural failure, such as the wrong heading depth or slot order. |
|
|
1959
|
-
| `"visitor"` | Semantic AST-construction failure, such as an invalid matcher dialect or signal shape. |
|
|
1960
|
-
|
|
1961
|
-
`severity` distinguishes a hard error from a non-fatal advisory. The parser is
|
|
1962
|
-
the sole and complete owner of syntax-error messaging because it holds the
|
|
1963
|
-
parse state, lexer mode, and expected-token set that no consumer has. It
|
|
1964
|
-
produces the final diagnostic message, deduplicated expected-token lists, and
|
|
1965
|
-
turn-shape diagnostics ({§turn-shape}). An omitted lifecycle declaration produces no diagnostic, and
|
|
1966
|
-
neither does the position of a present one ({§disposition-anywhere}). Source with no
|
|
1967
|
-
parsed operation yields `no valid Plurnk operation was found.` Targeted
|
|
1968
|
-
diagnostics are:
|
|
1969
|
-
|
|
1970
|
-
- §inline-flag-tolerance **PCRE inline modifiers.** A regex whose pattern opens with
|
|
1971
|
-
`(?i)`, `(?m)`, `(?s)` or a combination — the pretrained spelling of a flag, which
|
|
1972
|
-
ECMAScript refuses as an invalid group — is read with those letters lifted into
|
|
1973
|
-
its flags (`/(?i)shutdown|reactor/` is `/shutdown|reactor/i`; `^(?i)note:` is the
|
|
1974
|
-
anchored regex with `i`), with one warning-severity advisory naming the flag
|
|
1975
|
-
position after the closing `/`. Only a leading group of `i`, `m` and `s` lifts;
|
|
1976
|
-
`(?i:…)` scoped modifiers are valid ECMAScript and pass through untouched.
|
|
1977
|
-
`plurnk.md` teaches one regex spelling, `/\btimeout\b/i`, with flags after the closing
|
|
1978
|
-
slash; the lift is not taught. Unlike the tolerances on the {§outside-text}
|
|
1979
|
-
scale, this one forgives a departure the model did not choose: `(?i)` is the spelling
|
|
1980
|
-
a great many models were trained on, so refusing it would punish an instinct rather
|
|
1981
|
-
than a mistake ({§naked-pattern} carries `^` for the same reason). The advisory still
|
|
1982
|
-
names the canonical position, because here the model *can* learn a better spelling.
|
|
1983
|
-
- §regex-trailing-text A valid `/pattern/flags` prefix followed by horizontal
|
|
1984
|
-
whitespace and trailing text receives one concise trailing-content
|
|
1985
|
-
diagnostic, with or without flags, without assuming what the extra text was
|
|
1986
|
-
intended to represent — except `-flags`, which {§regex-dialect-readings} reads as
|
|
1987
|
-
the flags. Invalid patterns or flags retain the native regex failure; no branch
|
|
1988
|
-
silently removes or executes trailing content. Each diagnostic is the matcher's
|
|
1989
|
-
refusal ({§matcher-refusal}).
|
|
1990
|
-
- §regex-sed-range **A sed line range is named as one.** A regex matcher written as a sed
|
|
1991
|
-
address range — `/a/,/b/` or `/a/,+N` — is refused ({§matcher-refusal}) as a range, never as invalid flags: the
|
|
1992
|
-
diagnostic names the range suffix and gives the regex matcher's working form.
|
|
1993
|
-
It does not synthesize a different selector or a workflow ({§diagnostic-observation}).
|
|
1994
|
-
- §unclosed-regex **A regex that never closes.** A `/pattern` matcher with no closing
|
|
1995
|
-
`/` is read as the whole pattern with no flags, with one warning-severity advisory
|
|
1996
|
-
naming the closing slash and the flag position. The reading is deterministic because
|
|
1997
|
-
the heading's own boundaries bound it ({§naked-pattern}: the matcher runs to the end
|
|
1998
|
-
of the heading, and an aside is lexed apart from it). A bare `/` with nothing after
|
|
1999
|
-
it has no pattern to read and is refused as `invalid-operation-syntax` naming what
|
|
2000
|
-
is missing. A matcher with a second unescaped `/` is not unclosed: it is
|
|
2001
|
-
`/pattern/flags`, and invalid flags keep their native failure with the escape hint.
|
|
2002
|
-
- §naked-pattern **The matcher rides the heading bare.** After the path, and any scope or option block, the rest of a FIND, READ or KILL heading line is the matcher, in whichever dialect its first characters claim ({§matcher-prefix-claims}): `/re/i`, `^anchored`, `//xpath`, `$.json`, `~words`, `&symbol`, or a sigil-less glob or literal such as `TODO` or `*.ts`. `FIND (src/parser.ts) /\bparse\w+\b/` is the same operation as `FIND (src/parser.ts) [{"pattern": "/\\bparse\\w+\\b/"}]`: the lift is exactly what `[{"pattern": "…"}]` produces, and that option remains the escape for a matcher the heading cannot hold bare — one opening with `(`, `<`, `[` or a backtick, one containing `<!--`, and every COPY/MOVE operand.
|
|
2003
|
-
|
|
2004
|
-
| Fact | Rule |
|
|
2005
|
-
| --- | --- |
|
|
2006
|
-
| Not a matcher | `#name` alone, a `#` followed by one channel token and nothing else, after a target that names no channel, is that target's channel exactly as if written on the path ({§local-path-fragment}); a target that already names a channel keeps such a token as its pattern |
|
|
2007
|
-
| Independence | heading matchers remain independent of owner options: a block without `pattern` never erases the heading matcher, and invalid blocks still reach the owning validator; their heading-line text owns selection independently of annotation or distillation bodies |
|
|
2008
|
-
| EDIT | only a sigil lifts, because plain heading-line text is the replacement body it always was; the lines beneath the heading are then the replacement, and none deletes each match ({§edit-pattern}) |
|
|
2009
|
-
| The aside | a trailing `<!-- aside -->` on the same line stays the aside |
|
|
2010
|
-
| `^` | claims the regex dialect without slashes or flags: the whole text is the pattern, so `READ (notes.md) ^Decision:.*` selects lines beginning with `Decision:`; `^` is deliberately absent from `plurnk.md`'s dialect table — a model writes the anchor whether or not it is taught, so the engine honours it without spending hot-path teaching on it, and the omission is not drift between the teaching and the engine |
|
|
2011
|
-
|
|
2012
|
-
- §trailing-slots **Slots after the matcher peel off the right.** The heading text after the matcher is read backwards: a trailing `<!-- aside -->`, a trailing `<scope>` in the shapes the lexer admits (result positions on FIND, text coordinates elsewhere) and a trailing `[option block]` that parses as an array of objects come off the right end in any order, each taken once and only when the heading did not already carry that slot, until what remains is the matcher. `READ (a.rs) /fn resolve_/ <1,-1> <!-- entities -->` is the same operation as `READ (a.rs) <1,-1> /fn resolve_/ <!-- entities -->`, with one warning-severity advisory naming the canonical order for the scope or block.
|
|
2013
|
-
|
|
2014
|
-
| Fact | Rule |
|
|
2015
|
-
| --- | --- |
|
|
2016
|
-
| Recovery | requires a single possible path-slot owner: with multiple slots, a trailing scope or metadata block refuses the heading rather than relocating or discarding the slot |
|
|
2017
|
-
| A matcher that itself ends in one of those shapes | takes the option escape |
|
|
2018
|
-
| Teaching | `plurnk.md` teaches one order — `OP (path)? <scope\|range>? [metadata]? pattern? <!-- aside -->?` — and free ordering is not taught; small departure, small reinterpretation ({§outside-text}): every slot the model wrote is present and unambiguous, so only their sequence differs from the taught form, and nothing is invented to read it; the advisory names the canonical order rather than refusing, because the operation the model meant is never in doubt |
|
|
2019
|
-
|
|
2020
|
-
- §log-heading-notation **The log's heading notation, copied as syntax, is read as the slot it
|
|
2021
|
-
stands for.** A packet's log row heading is `### log:///L/T/S/OP → path pattern · N`
|
|
2022
|
-
({§log-wire-format} in the core SPEC); models copy its notation onto their own headings. Each
|
|
2023
|
-
piece has one reading, so each is read and named with one warning-severity advisory after its
|
|
2024
|
-
statement:
|
|
2025
|
-
|
|
2026
|
-
| Written | Read as | Advisory |
|
|
2027
|
-
|---|---|---|
|
|
2028
|
-
| `READ → sh:///x#stdout <1,50>` | the target, `READ (sh:///x#stdout) <1,50>`; COPY/MOVE take one arrow per operand | `` `→ sh:///x#stdout` is how the log shows an address; it was read as the target. Write the target in parentheses: `READ (sh:///x#stdout) <1,50>`. `` |
|
|
2029
|
-
| `READ (a.py) <1,50> · 900`, `· 320 tokens`, a bare `·` | nothing: the token charge is dropped, on the heading or after a matcher | `` `· 900` is the token charge the log shows on a heading; it is not part of an operation and was ignored. `` |
|
|
2030
|
-
| `FIND (x) /re/ · locate the sign handling` | the aside | `` `· locate the sign handling` was read as the aside; a note on an operation is written `<!-- locate the sign handling -->`. `` |
|
|
2031
|
-
| `FIND (x) <84,180> · no <!-- read the helpers -->` | the comment is the aside; the dot note before it is stray text, dropped | `` `· no` was ignored; the aside is `<!-- read the helpers -->`. `` |
|
|
2032
|
-
|
|
2033
|
-
The arrow's path runs to the next space, `<` or `[`; a `·` never begins a heading matcher. In
|
|
2034
|
-
distinct recorded benchmark emissions the arrow form opened 40 headings and the charge ended 52,
|
|
2035
|
-
where they drew a missing-target 400 or a false-empty glob match.
|
|
2036
|
-
- §bare-target **A target written without its parentheses is refused with the line that runs.**
|
|
2037
|
-
A FIND, READ or EDIT heading with no target whose heading text opens with a word that is no
|
|
2038
|
-
matcher sigil, `READ a.py <1,4>`, cannot run: the word stands where the target goes, but on FIND it
|
|
2039
|
-
could as well be a pattern. The statement is one hard diagnostic,
|
|
2040
|
-
`` `READ` has no target: `a.py` stands where the target goes. ``, whose recovery is the operation's
|
|
2041
|
-
working form ({§parse-recovery}).
|
|
2042
|
-
- §bare-anchor-scope **An EDIT's anchor without its angle brackets is its scope.** On an EDIT
|
|
2043
|
-
heading, `@abcde` or `@abcde,@fghij` standing alone where the scope goes — nothing but an aside, a
|
|
2044
|
-
closer or the line end after it — is the scope `<@abcde>`, with one warning-severity advisory:
|
|
2045
|
-
`` `@abcde` was read as the scope `<@abcde>`; a scope is written in angle brackets. `` Read as
|
|
2046
|
-
body instead, the anchor would be written into the file, or the EDIT refused for want of a line
|
|
2047
|
-
marker (14 distinct recorded headings). On READ and KILL the same text stays a literal matcher,
|
|
2048
|
-
since `@patch` is a search a model means.
|
|
2049
|
-
- §matcher-body-redirect **Unread selector text.** An option block's `pattern` owns the
|
|
2050
|
-
selector; surplus heading text is unused with one warning naming its location.
|
|
2051
|
-
A non-log KILL's body is likewise unused: `This KILL takes no body; line 2 was not used.`
|
|
2052
|
-
Neither warning echoes discarded text. No operation recovers a matcher from below
|
|
2053
|
-
its header. READ/FIND bodies follow {§retrieval-annotation}; log KILL bodies follow
|
|
2054
|
-
{§log-kill-distillation}.
|
|
2055
|
-
- §combined-anchor-tolerance **Combined line number and anchor in a scope.** A
|
|
2056
|
-
text-coordinate scope written `L<@hash>` — digits immediately before the opener — is the
|
|
2057
|
-
displayed `42<@abcde>` row prefix copied whole: the scope is the anchor, the number is dropped, and one
|
|
2058
|
-
warning-severity advisory names the anchor-only form. The scope lexes as one
|
|
2059
|
-
ordinary marker at any text-coordinate operation, either COPY/MOVE operand
|
|
2060
|
-
included; nothing cascades.
|
|
2061
|
-
- §invalid-scope-diagnostic **Malformed scope content.** After a properly spaced
|
|
2062
|
-
scope opener, report the offending scope (at most 64 code points, ending at
|
|
2063
|
-
`>` or the heading's line end) and its operation's constraint: FIND result
|
|
2064
|
-
positions, text coordinates, or no scope. Do not append advice for
|
|
2065
|
-
other operations or infer why the producer supplied the value. A text-coordinate
|
|
2066
|
-
scope that pairs an anchor with the same five characters unmarked (`<@abcde,fghij>`)
|
|
2067
|
-
states the anchor rule instead, with the scope marked: `` every line anchor carries
|
|
2068
|
-
its `@`: `<@abcde,@fghij>` ``. Spacing and boundary-loss diagnostics retain their own
|
|
2069
|
-
contracts.
|
|
2070
|
-
|
|
2071
|
-
§error-shape The diagnostic class determines the parts of a message; every class says what
|
|
2072
|
-
happened, never why ({§diagnostic-observation}). Advisories belong to one successfully built statement. A rejected
|
|
2073
|
-
statement emits its hard diagnostic, not normalization advisories; neither a
|
|
2074
|
-
rejection nor an internal exception carries advisories into another statement
|
|
2075
|
-
or parser invocation.
|
|
2076
|
-
|
|
2077
|
-
| Class | Surface | Message contract |
|
|
2078
|
-
|---------------|-----------------------|-------------------------------------------------------------------------------------------------------------------|
|
|
2079
|
-
| 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}). |
|
|
2080
|
-
| 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. |
|
|
2081
|
-
| Boundary loss | `unparsedTail` | Where trust ends and which header slot remains open; later input is undefined. |
|
|
2082
|
-
|
|
2083
|
-
All messages use PLURNK protocol vocabulary: opening fence, closing fence, target,
|
|
2084
|
-
scope, line marker, body, section boundary, or space between slots. They never
|
|
2085
|
-
expose ANTLR rule or token names. They refer to a slot or
|
|
2086
|
-
feature rather than an implementation rule. Generic tutoring, coordinate
|
|
2087
|
-
restatement and multiple repair strategies are forbidden, and no message names an
|
|
2088
|
-
intent ({§diagnostic-observation}).
|
|
2089
|
-
Unexpected top-level text immediately after a closed operation identifies that
|
|
2090
|
-
operation's opening line, closing line, and matching backtick count.
|
|
2091
|
-
|
|
2092
|
-
§parse-attempt A hard fact that refused a statement the parser opened carries its `attempt`: the
|
|
2093
|
-
statement's text exactly as written, from its opening fence as far as the parser read it, and the
|
|
2094
|
-
`verb` that fence names, an operation keyword or an execution's runtime tag. A diagnostic that refused no opened
|
|
2095
|
-
statement, such as a lost boundary or a stray character outside every statement, carries none.
|
|
2096
|
-
|
|
2097
|
-
Examples of canonical hard facts:
|
|
2098
|
-
|
|
2099
|
-
- `unrecognized character '<' in target`
|
|
2100
|
-
- `unrecognized character '[' in the sh header`
|
|
2101
|
-
- `unrecognized character 'X' in statement header`
|
|
2102
|
-
- `unrecognized character '[' in the WAIT header`
|
|
2103
|
-
- `expected ')'; got ':'`
|
|
2104
|
-
|
|
2105
|
-
Each malformed statement produces at most one hard error. The first recorded
|
|
2106
|
-
hard lexer or parser error within its source range wins; later failures in that
|
|
2107
|
-
same range are consumed rather than projected as a cascade. A visitor failure
|
|
2108
|
-
surfaces when syntax admitted the statement but AST construction did not.
|
|
2109
|
-
Independent malformed statements each retain one hard error. Advisories remain
|
|
2110
|
-
separate because they do not represent failed admission.
|
|
2111
|
-
|
|
2112
|
-
§unparsed-tail-boundary When the lexer cannot determine where a malformed
|
|
2113
|
-
statement ends — an unfinished `(target` or `[metadata` slot on a heading line —
|
|
2114
|
-
the result's `unparsedTail` marks the position from which parsing gave up. A block
|
|
2115
|
-
without a closer is not such a case: it ends under {§closer-fallback}. `ParseResult.items` contains only facts that begin strictly
|
|
2116
|
-
before that point; recovered contexts and diagnostics at or beyond it are not
|
|
2117
|
-
public results. The tail is one separate boundary fact, not an additional
|
|
2118
|
-
malformed-statement diagnostic. Consumers must treat anything from that point
|
|
2119
|
-
onward as undefined and must never dispatch a recovered statement from it; the
|
|
2120
|
-
facts before it are ordinary facts, and a consumer that runs them owes the
|
|
2121
|
-
author the tail's reason.
|
|
2122
|
-
|
|
2123
|
-
| Consumer duty | Contract |
|
|
2124
|
-
|--------------------|----------------------------------------------------------------------------------------------------------------|
|
|
2125
|
-
| Diagnostic text | Project `message` verbatim; do not strip prefixes, restate coordinates, or synthesize generic syntax recovery. |
|
|
2126
|
-
| Structured context | Preserve `line`, `column`, `source`, and `severity` as separate fields. |
|
|
2127
|
-
| Runtime recovery | Project the parser's `recovery` ({§parse-recovery}) and attach only a separately owned fact, such as Core knowing that bounded sibling operations were retained. |
|
|
2128
|
-
| Durable projection | Map bounded hard errors to failed operation results; warnings may become Notices with `level: "warn"`. |
|
|
2129
|
-
| Presentation | Normalize or bound the diagnostic only when the surface requires it, without changing its meaning. |
|
|
2130
|
-
|
|
2131
|
-
Serialization convention for transmission to the model (the agent
|
|
2132
|
-
runtime constructs this; the parser provides the fields):
|
|
2133
|
-
|
|
2134
|
-
```json
|
|
2135
|
-
{
|
|
2136
|
-
"line": 1,
|
|
2137
|
-
"column": 12,
|
|
2138
|
-
"source": "parser",
|
|
2139
|
-
"severity": "error",
|
|
2140
|
-
"message": "READ block opened at line 1 but was not closed with 3 backticks"
|
|
2141
|
-
}
|
|
2142
|
-
```
|
|
243
|
+
| Working-form recovery & attempt | Every hard error (`severity: "error"`) populates `recovery` with the canonical syntax of the refused construct and `attempt` (`{ text, verb }`) with the refused statement's source text; `operationOmitted` marks unexecuted unfenced or native-call fragments. Each malformed statement produces at most one hard error. |
|
|
244
|
+
| Regex and matcher tolerances | Lifts leading PCRE inline flags `(?ims)` with a warning; folds comma-separated `/pattern/` lists on `FIND` and `READ` into alternation with a warning while refusing numeric/open sed ranges (`/a/,+N`, `/a/,-1`, `/a/,`) and mutating `/a/,/b/` ranges; diagnoses trailing text after `/pattern/flags`; recovers unclosed `/pattern` at EOL; flags unused non-log `KILL` body lines (`This KILL takes no body; line 2 was not used.`). |
|
|
245
|
+
| Heading & slot tolerances | Reads bare heading patterns (`naked-pattern`, treating bare `#channel` after a channelless target as its channel fragment), peels trailing `<scope>`/`[metadata]`/`<!-- aside -->` slots from the right of a matcher when the heading has a single target, refuses unparenthesized bare targets (`READ a.py <1,4>`), recovers bare `EDIT` anchors (`@abcde` -> `<@abcde>`) and combined `42<@abcde>` scopes, and sets `unparsedTail` when an unclosed `(target` or `[metadata` destroys statement boundaries. |
|
|
246
|
+
| Log-heading notation recovery | Recovers copied log-heading forms with a warning advisory: `OP → path <scope>` is read as `(path)`; `· 900` / `· 320 tokens` token charges are dropped; `· note text` at the end of a heading is read as `<!-- note text -->` (or dropped when an explicit `<!-- aside -->` follows). |
|