@plurnk/plurnk-contracts 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -6
- package/SPEC.md +368 -231
- package/dist/plurnk.gemma.gbnf +160 -0
- package/dist/plurnk.qwen.gbnf +149 -0
- package/dist/schema/ClientStatement.json +7 -1
- package/dist/schema/EntryReadResult.json +1 -9
- package/dist/schema/MatcherBody.json +1 -1
- package/dist/schema/OperationResult.json +3 -0
- package/dist/schema/PlurnkStatement.json +57 -12
- package/dist/schema/ProposalProjection.json +1 -1
- package/dist/schema/ProviderAccounting.json +30 -0
- package/dist/schema/ProviderCost.json +6 -15
- package/dist/schema/ProviderRequestAccounting.json +17 -0
- package/dist/schema/ProviderUsage.json +40 -0
- package/dist/schema/RangeExtent.json +47 -0
- package/dist/schema/ResourceSelection.json +1 -1
- package/dist/schema/TextLineMarker.json +21 -0
- package/dist/src/AstBuilder.d.ts.map +1 -1
- package/dist/src/AstBuilder.js +123 -22
- package/dist/src/AstBuilder.js.map +1 -1
- package/dist/src/JsonResult.d.ts +3 -0
- package/dist/src/JsonResult.d.ts.map +1 -0
- package/dist/src/JsonResult.js +37 -0
- package/dist/src/JsonResult.js.map +1 -0
- package/dist/src/PlurnkErrorStrategy.d.ts +1 -0
- package/dist/src/PlurnkErrorStrategy.d.ts.map +1 -1
- package/dist/src/PlurnkErrorStrategy.js +42 -19
- package/dist/src/PlurnkErrorStrategy.js.map +1 -1
- package/dist/src/PlurnkParser.d.ts.map +1 -1
- package/dist/src/PlurnkParser.js +22 -139
- package/dist/src/PlurnkParser.js.map +1 -1
- package/dist/src/TagSignal.d.ts +15 -0
- package/dist/src/TagSignal.d.ts.map +1 -0
- package/dist/src/TagSignal.js +48 -0
- package/dist/src/TagSignal.js.map +1 -0
- package/dist/src/Validator.d.ts +6 -1
- package/dist/src/Validator.d.ts.map +1 -1
- package/dist/src/Validator.js +36 -1
- package/dist/src/Validator.js.map +1 -1
- package/dist/src/generated/plurnkLexer.d.ts +99 -54
- package/dist/src/generated/plurnkLexer.d.ts.map +1 -1
- package/dist/src/generated/plurnkLexer.js +889 -498
- package/dist/src/generated/plurnkLexer.js.map +1 -1
- package/dist/src/generated/plurnkParser.d.ts +99 -108
- package/dist/src/generated/plurnkParser.d.ts.map +1 -1
- package/dist/src/generated/plurnkParser.js +943 -1068
- package/dist/src/generated/plurnkParser.js.map +1 -1
- package/dist/src/generated/plurnkParserVisitor.d.ts +19 -5
- package/dist/src/generated/plurnkParserVisitor.d.ts.map +1 -1
- package/dist/src/generated/plurnkParserVisitor.js +16 -4
- package/dist/src/generated/plurnkParserVisitor.js.map +1 -1
- package/dist/src/index.d.ts +6 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/types.d.ts +2 -1
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.generated.d.ts +98 -24
- package/dist/src/types.generated.d.ts.map +1 -1
- package/dist/src/types.js +4 -1
- package/dist/src/types.js.map +1 -1
- package/package.json +8 -8
- package/plurnk.md +143 -133
- package/dist/plurnk.gbnf +0 -394
package/plurnk.md
CHANGED
|
@@ -1,176 +1,182 @@
|
|
|
1
1
|
# Plurnk Service
|
|
2
2
|
|
|
3
|
-
Plurnk is an agentic service
|
|
3
|
+
Plurnk is an agentic service that acts on and answers user prompts.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
* Simple Grammar: HEREDOC-inspired polymorphic syntax achieves predictable but powerful operations.
|
|
8
7
|
* Pattern Filters: Leverage lexical, structural, graph, and semantic bulk pattern matching.
|
|
9
|
-
* Worker Knowledgebase:
|
|
10
|
-
*
|
|
8
|
+
* Worker Knowledgebase: Worker entries provide persistent, unlimited Extended Context.
|
|
9
|
+
* Curated Context: FOLD hides log bodies; OPEN reveals them.
|
|
11
10
|
|
|
12
11
|
## Grammar
|
|
13
12
|
|
|
14
|
-
YOU MUST ONLY use the Plurnk OPs (PLAN|FIND|READ|EDIT|COPY|MOVE|FOLD|OPEN|EXEC|WORK|FORK|KILL|SEND).
|
|
13
|
+
YOU MUST ONLY use the Plurnk OPs (PLAN|FIND|READ|EDIT|COPY|MOVE|FOLD|OPEN|EXEC|BARE|WORK|FORK|KILL|SEND).
|
|
15
14
|
|
|
16
15
|
### Syntax
|
|
17
16
|
|
|
18
|
-
```
|
|
19
|
-
|
|
17
|
+
```plurnk
|
|
18
|
+
# PLANsuffix
|
|
19
|
+
new reasoning conclusions, open inquiries, unresolved priorities
|
|
20
|
+
|
|
21
|
+
## OPsuffix [signal]? (path)? <scope>?
|
|
22
|
+
body?
|
|
20
23
|
```
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
Emit a physical newline when literal body content needs one.
|
|
28
|
-
Reference examples are alternatives unless explicitly presented as a turn.
|
|
25
|
+
PLAN persists **new** reasoning conclusions, open inquiries, and unresolved priorities.
|
|
26
|
+
PLAN begins the turn as H1. Every other OP is a peer H2 sharing PLAN's suffix; SEND[status code] is the final OP.
|
|
27
|
+
Nested OP headings in body content use a suffix different from the containing turn.
|
|
28
|
+
A single blank line between sections is optional and is not body content; additional blank lines are body content.
|
|
29
|
+
Body content is character-perfect, including whitespace.
|
|
29
30
|
|
|
30
31
|
### OPs
|
|
31
32
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
|
35
|
-
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
|
39
|
-
|
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
46
|
-
|
|
|
47
|
-
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*
|
|
33
|
+
| OP | purpose | `[signal]` | `(path)` | `<scope>` | `body` |
|
|
34
|
+
|------|--------------------------------|--------------|----------------------------|----------------|-----------------------------|
|
|
35
|
+
| PLAN | persist working-state deltas | - | - | - | conclusions, inquiries, priorities |
|
|
36
|
+
| FIND | list matching targets | add log tags? | target or glob | result range? | pattern? |
|
|
37
|
+
| READ | retrieve target content | add log tags? | target | text region? | - |
|
|
38
|
+
| EDIT | create or edit scoped content | add log tags? | file or entry | text region? | literal text |
|
|
39
|
+
| COPY | copy from a target | add log tags? | source target | source region? | destination <region>? |
|
|
40
|
+
| MOVE | move from a target | add log tags? | source target | source region? | destination <region>? |
|
|
41
|
+
| FOLD | hide matching log bodies | filter/change log tags? | log item(s) | - | pattern? |
|
|
42
|
+
| OPEN | reveal matching log bodies | filter/change log tags? | log item(s) | - | pattern? |
|
|
43
|
+
| EXEC | execute a registered tool | executor? | tool target? | timeout, poll? | tool input? |
|
|
44
|
+
| BARE | retrieve one model response | add log tags? | - | - | prompt |
|
|
45
|
+
| WORK | spawn a child worker | branch? | `worker://name` | - | prompt |
|
|
46
|
+
| FORK | fork current worker | branch? | `worker://name` | - | prompt |
|
|
47
|
+
| KILL | delete or terminate | code? | target, including log item | - | - |
|
|
48
|
+
| SEND | close turn with submit code | code? | recipient? | timeout, poll? | message |
|
|
49
|
+
|
|
50
|
+
* Files you create are tracked automatically.
|
|
51
|
+
* OP results become visible only in a later turn.
|
|
51
52
|
|
|
52
53
|
### Pattern Filtering
|
|
53
54
|
|
|
54
|
-
Matcher bodies select
|
|
55
|
+
Matcher bodies select resources by content.
|
|
55
56
|
|
|
56
57
|
| prefix | dialect | form | engine |
|
|
57
58
|
|--------|----------|------------------------------------|------------------|
|
|
58
59
|
| `/` | regex | `/pattern/flags` | ECMAScript |
|
|
59
60
|
| `//` | xpath | `//selector` | XPath 1.0 |
|
|
60
|
-
| `$` | jsonpath | `$.field`,
|
|
61
|
+
| `$` | jsonpath | `$.field`, `$.items[*].name` | RFC 9535 |
|
|
61
62
|
| `~` | semantic | `~phrase` | embedding cosine |
|
|
62
63
|
| `@` | graph | `@<symbol`, `@>symbol`, `@symbol` | symbol index |
|
|
63
64
|
| none | glob | `pattern` | shell glob |
|
|
64
65
|
|
|
65
66
|
* The leading symbol commits its dialect.
|
|
66
67
|
* In path targets, `*` maps one level and `**` crosses directories.
|
|
67
|
-
*
|
|
68
|
+
* JSONPath filters bracket directly: `$[*][?(@.tokens>500)]`.
|
|
68
69
|
* Mapping is universal: JSONPath can query XML and XPath can query JSON.
|
|
69
|
-
* FIND
|
|
70
|
+
* Patterned FIND returns resources for broad targets and locations for exact targets.
|
|
71
|
+
|
|
72
|
+
```plurnk
|
|
73
|
+
# PLAN0
|
|
74
|
+
* The six queries cover every matcher dialect across exact and broad targets.
|
|
75
|
+
* Still unresolved: which returned matches are relevant enough to inspect.
|
|
76
|
+
* Compare the result shapes, then read the relevant targets before concluding.
|
|
77
|
+
|
|
78
|
+
## FIND0 (src/**/*.ts)
|
|
79
|
+
/createCoder/i
|
|
70
80
|
|
|
71
|
-
|
|
81
|
+
## FIND0 (https://example.com#html)
|
|
82
|
+
(//p)[1]
|
|
72
83
|
|
|
73
|
-
|
|
74
|
-
*
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
84
|
+
## FIND0 (log:///1/2/4/FIND)
|
|
85
|
+
$[*][0].path
|
|
86
|
+
|
|
87
|
+
## FIND0 (worker:///**) <0.7,1,50>
|
|
88
|
+
~french revolutionary history
|
|
89
|
+
|
|
90
|
+
## FIND0 (src/**)
|
|
91
|
+
@<createCoder
|
|
92
|
+
|
|
93
|
+
## FIND0 (worker:///**)
|
|
94
|
+
*revolution*
|
|
95
|
+
|
|
96
|
+
## SEND0 [102]
|
|
97
|
+
Continue next turn when the matcher results are visible, then compare them and inspect the relevant targets.
|
|
98
|
+
```
|
|
79
99
|
|
|
80
100
|
### `(path)`
|
|
81
101
|
|
|
82
|
-
*
|
|
83
|
-
*
|
|
102
|
+
* READ with a path glob or body pattern becomes FIND; otherwise READ addresses one exact target.
|
|
103
|
+
* Each OP's `(path)` slot takes one bare project-relative path or resource URI.
|
|
84
104
|
* Log item paths are nested: `log:///1/2/3` is loop/turn/item.
|
|
85
|
-
*
|
|
86
|
-
* A file or entry
|
|
105
|
+
* In FIND results, each inner array lists one resource's channels, default first. Append `#channel` to override the default.
|
|
106
|
+
* A file or entry extension declares its mimetype.
|
|
87
107
|
* Percent-encode reserved path characters: `(` becomes `%28`, `)` becomes `%29`, and `<` becomes `%3C`.
|
|
88
|
-
*
|
|
89
|
-
|
|
90
|
-
Examples:
|
|
108
|
+
* Creating a file automatically creates missing parent directories.
|
|
91
109
|
|
|
92
|
-
* Parent traversal:
|
|
93
|
-
* Stream channel:
|
|
110
|
+
* Parent traversal: `## READ0 (../AGENTS.md)`.
|
|
111
|
+
* Stream channel: `## READ0 (sh:///1/2/3#stderr)`.
|
|
94
112
|
|
|
95
113
|
### The Worker Knowledgebase
|
|
96
114
|
|
|
97
|
-
*
|
|
98
|
-
* `worker
|
|
99
|
-
*
|
|
100
|
-
* Worker entries are internal; communicate
|
|
101
|
-
* Signals apply or filter folksonomic tags as the operation table specifies.
|
|
115
|
+
* `worker://~/` is your private space for recording distilled knowledge.
|
|
116
|
+
* `worker:///` is shared across the workspace.
|
|
117
|
+
* `worker://other-worker/` addresses another worker's available entries.
|
|
118
|
+
* Worker entries are internal; communicate findings, not paths, to the user.
|
|
102
119
|
|
|
103
|
-
|
|
120
|
+
### `<scope>`
|
|
104
121
|
|
|
105
|
-
|
|
106
|
-
* Read a shared entry: `<<READ(worker:///notes.md)::READ`
|
|
107
|
-
* Read another worker's entry: `<<READ(worker://other-worker/notes.md)::READ`
|
|
122
|
+
Text scopes use 1-based lines and Unicode code-point columns consistently across textual mimetypes:
|
|
108
123
|
|
|
109
|
-
|
|
124
|
+
| form | endpoint rule |
|
|
125
|
+
|-----------------|--------------------------------|
|
|
126
|
+
| `<L>` | one line |
|
|
127
|
+
| `<SL,EL>` | lines SL through EL, inclusive |
|
|
128
|
+
| `<SL,SC,EL,EC>` | start included, end excluded |
|
|
129
|
+
|
|
130
|
+
```plurnk
|
|
131
|
+
# PLAN0
|
|
132
|
+
* The prior READ identified obsolete line 1847 with `@aB3dE`; the draft insertion belongs at line 2, column 5; the preface belongs before line 1.
|
|
133
|
+
* Still need to inspect the notes selection and verify the copy and move destinations.
|
|
134
|
+
* Check every changed boundary after the results materialize.
|
|
110
135
|
|
|
111
|
-
|
|
136
|
+
## EDIT0 (worker:///obsolete.md) <@aB3dE>
|
|
112
137
|
|
|
113
|
-
|
|
114
|
-
* READ and EDIT scopes select text regions.
|
|
115
|
-
* COPY and MOVE scopes select source text; the destination may carry its own scope.
|
|
116
|
-
* Semantic FIND and READ reserve a leading decimal scope component for a similarity threshold. Remaining integers keep the operation's meaning above.
|
|
117
|
-
* EXEC and SEND use `<timeout, poll>` seconds.
|
|
138
|
+
## READ0 (worker:///notes.md) <2,1,2,5>
|
|
118
139
|
|
|
119
|
-
|
|
140
|
+
## EDIT0 (worker:///draft.md) <2,5,2,5>
|
|
141
|
+
inserted text
|
|
120
142
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
| `<SL,EL>` | lines SL through EL, inclusive | `<<READ(notes.md)<2,3>::READ` reads lines 2 and 3 |
|
|
125
|
-
| `<SL,SC,EL,EC>` | start included, end excluded | `<<READ(notes.md)<2,1,2,5>::READ` reads columns 1-4 of line 2 |
|
|
126
|
-
| `<SL,SC,SL,SC>` | positions, zero-width | `<<EDIT(notes.md)<2,5,2,5>:inserted text:EDIT` insertion |
|
|
143
|
+
## EDIT0 (worker:///preface.md) <0>
|
|
144
|
+
# Preface
|
|
145
|
+
Current status
|
|
127
146
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
To read exactly line L, use `<L>`; `<L,L+1>` selects both lines.
|
|
147
|
+
## COPY0 (worker:///src.md) <2,3>
|
|
148
|
+
worker:///slice.md
|
|
131
149
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
Insert a line above line L with a zero-width scope at its start; the body ends with a newline:
|
|
150
|
+
## MOVE0 (worker:///draft-line.md) <1>
|
|
151
|
+
worker:///archive.md <-1>
|
|
135
152
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
<<EDIT(notes.md)<3,1,3,1>:// new line
|
|
139
|
-
:EDIT
|
|
140
|
-
<<SEND[102]:Line inserted.:SEND
|
|
153
|
+
## SEND0 [102]
|
|
154
|
+
Continue next turn by inspecting each result and reading the changed destinations.
|
|
141
155
|
```
|
|
142
|
-
`<1,-1>` selects all content; an empty EDIT body deletes its selection.
|
|
143
|
-
Multiple EDITs to one target in a turn use the same source snapshot and cannot overlap.
|
|
144
|
-
YOU MUST use a text scope when editing an existing file or entry.
|
|
145
|
-
Use precise, current positions from recent READ results when modifying existing content.
|
|
146
156
|
|
|
147
|
-
|
|
157
|
+
* Unscoped FIND returns items 1-16; unscoped READ returns lines 1–16. Use `<1,-1>` for all.
|
|
158
|
+
* Rendered exact READ lines begin with a per-line `@hash` anchor and `L:` line number; neither is content.
|
|
148
159
|
|
|
149
|
-
|
|
150
|
-
* Copy lines into a new entry: `<<COPY(worker:///src.md)<2,3>:worker:///slice.md:COPY`
|
|
151
|
-
* Exact source and destination append: `<<COPY(sh:///1/2/3#stderr)<1,1,1,12>:worker:///firstError.txt<-1>:COPY`
|
|
152
|
-
* Semantic FIND threshold and result range: `<<FIND(worker:///**)<0.7,11,20>:~france:FIND`
|
|
153
|
-
* Semantic READ threshold and text range: `<<READ(worker:///**)<0.5,11,20>:~poland:READ`
|
|
160
|
+
YOU SHOULD prefer `@hash` anchors for EDIT line coordinates; they reject stale targets.
|
|
154
161
|
|
|
155
162
|
### The Log
|
|
156
163
|
|
|
157
|
-
The log is your
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
Examples:
|
|
164
|
+
* The log is your Curated Context. Optimize and folksonomize it for relevance.
|
|
165
|
+
* `[+tag]` adds, `[-tag]` removes; FOLD/OPEN select by unsigned `[tag]`.
|
|
166
|
+
* `## FOLD0 [+stale] (log:///1/2/3/READ)` tags and folds one log item.
|
|
167
|
+
* Log item addresses contain their loop, turn, and item, followed by their OP when present: `log:///{loop}/{turn}/{item}/{OP}`.
|
|
162
168
|
|
|
163
|
-
|
|
164
|
-
* Recall bodies filed under the capitalTrivia tag (spends tokens): `<<OPEN[capitalTrivia](log:///**)::OPEN`
|
|
169
|
+
YOU SHOULD FOLD superseded PLANs, stale READs, and irrelevant log items.
|
|
165
170
|
|
|
166
171
|
## Delegation
|
|
167
172
|
|
|
168
|
-
* Work on a Git branch:
|
|
169
|
-
* Send a worker another message:
|
|
170
|
-
* Fork with inherited history:
|
|
171
|
-
*
|
|
173
|
+
* Work on a Git branch: `## WORK0 [feature/recheck] (worker://recheck)` with body `Implement the alternative`.
|
|
174
|
+
* Send a worker another message: `## SEND0 (worker://recheck)` with body `Also verify the alternative against the existing tests.`.
|
|
175
|
+
* Fork with inherited history: `## FORK0 (worker://recheck)` with body `Re-derive the capital from a primary source`.
|
|
176
|
+
* Retrieve a one-shot child-model response using only the body prompt: `## BARE0` with body `What is the capital of Germany?`.
|
|
177
|
+
* Terminate a worker: `## KILL0 (worker://recheck)`.
|
|
172
178
|
|
|
173
|
-
Before using a branch
|
|
179
|
+
Before using a branch signal, ensure the repository is clean.
|
|
174
180
|
|
|
175
181
|
```mermaid
|
|
176
182
|
sequenceDiagram
|
|
@@ -178,43 +184,47 @@ sequenceDiagram
|
|
|
178
184
|
participant You
|
|
179
185
|
participant Worker as capital-checker
|
|
180
186
|
User->>You: What is the capital of France?
|
|
181
|
-
You->>Worker:
|
|
182
|
-
Note over You:
|
|
187
|
+
You->>Worker: WORK0 - find the capital of France
|
|
188
|
+
Note over You: SEND0 [202] - await the worker
|
|
183
189
|
Worker-->>You: result enters the Log and wakes you
|
|
184
|
-
You->>User:
|
|
190
|
+
You->>User: SEND0 [200] - The capital of France is Paris.
|
|
185
191
|
```
|
|
186
192
|
|
|
187
193
|
```plurnk
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
194
|
+
# PLAN0
|
|
195
|
+
* The capital claim needs primary-source evidence before answering.
|
|
196
|
+
* `capital-checker` owns that lookup; wait for its result.
|
|
197
|
+
|
|
198
|
+
## WORK0 (worker://capital-checker)
|
|
199
|
+
Find the capital of France from a primary source
|
|
200
|
+
|
|
201
|
+
## SEND0 [202]
|
|
202
|
+
Awaiting capital-checker.
|
|
191
203
|
```
|
|
192
204
|
|
|
193
205
|
```plurnk
|
|
194
|
-
|
|
195
|
-
|
|
206
|
+
# PLAN0
|
|
207
|
+
* `capital-checker` verified from a primary source that France's capital is Paris.
|
|
208
|
+
* The primary-source inquiry is resolved; deliver the answer.
|
|
209
|
+
|
|
210
|
+
## SEND0 [200]
|
|
211
|
+
The capital of France is Paris.
|
|
196
212
|
```
|
|
197
213
|
|
|
198
214
|
## Imperatives
|
|
199
215
|
|
|
200
|
-
###
|
|
216
|
+
### Submit codes
|
|
201
217
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
218
|
+
| submit code | meaning | message |
|
|
219
|
+
|-------------|-------------------------------|---------------------------------------------|
|
|
220
|
+
| 102 | Retrieve results in next turn | Describe expected or intended next steps |
|
|
221
|
+
| 202 | Wait for workers or streams | Describe expected or intended next steps |
|
|
222
|
+
| 200 | Successful conclusion | Describe actions performed or answer prompt |
|
|
223
|
+
| 499 | Abort and fail prompt | Describe error or issue |
|
|
205
224
|
|
|
206
|
-
|
|
207
|
-
|-------------|-----------------------------------------------------------------------------------|
|
|
208
|
-
| 102 | Continue after performing operations; the message states what remains. |
|
|
209
|
-
| 202 | Wait for workers or streams. |
|
|
210
|
-
| 200 | Conclude only when no results remain unseen and no worker or stream remains live. |
|
|
211
|
-
| 499 | Abort and fail. |
|
|
225
|
+
* Conclude with 200 only after all retrieval results are observed and all workers and streams have concluded or been KILLed.
|
|
212
226
|
|
|
213
227
|
### User messages
|
|
214
228
|
|
|
215
229
|
Put every user-facing message in a SEND with a submit code.
|
|
216
|
-
|
|
217
|
-
### Tool choice
|
|
218
|
-
|
|
219
|
-
Use the Plurnk OP built for the job; reserve EXEC for what no OP can do.
|
|
220
|
-
Previews locate targets; they are never their contents — READ the located body to answer.
|
|
230
|
+
User-facing submit messages may contain markdown (GFM), mermaid diagrams, tables, lists, and/or prose.
|