@sjawhar/pi-envoy 8.3.0 → 8.5.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 +38 -31
- package/bin/dispatch +6 -0
- package/dist/dispatch.js +18648 -0
- package/dist/envoy.js +580 -2656
- package/dist/skills/dispatch/SKILL.md +85 -97
- package/dist/skills/dispatch/references/api.md +4 -4
- package/dist/skills/dispatch/references/asks.md +34 -54
- package/dist/skills/dispatch/references/document-edits.md +24 -21
- package/dist/skills/dispatch/references/documents.md +75 -72
- package/dist/skills/dispatch/references/examples.md +37 -67
- package/dist/skills/dispatch/references/issues.md +48 -48
- package/dist/skills/dispatch/references/messages.md +18 -25
- package/dist/skills/dispatch/references/reading.md +10 -10
- package/dist/skills/dispatch-brainstorming/SKILL.md +19 -15
- package/dist/skills/dispatch-first/SKILL.md +7 -7
- package/dist/skills/envoy/SKILL.md +3 -2
- package/package.json +6 -5
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
# Editing a document
|
|
2
2
|
|
|
3
|
-
Every document
|
|
4
|
-
with `
|
|
5
|
-
the
|
|
3
|
+
Every document Dispatch holds — an issue's spec, a project document — is edited in place
|
|
4
|
+
with `dispatch doc-edit`, never re-uploaded. "The Spec" in `skill://dispatch` sends you here for
|
|
5
|
+
the command's shape, how to target the text you mean, what each operation costs a block, and how to
|
|
6
6
|
reject a stale edit.
|
|
7
7
|
|
|
8
|
-
```
|
|
9
|
-
|
|
8
|
+
```bash
|
|
9
|
+
dispatch doc-edit --issue <KEY> --artifact spec --summary '<names the version>' --ops-json-file - <<'EOF'
|
|
10
|
+
[{"op": "replace", "find": "old sentence", "with": "new sentence"}]
|
|
11
|
+
EOF
|
|
10
12
|
```
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
`--project` replaces `--issue` for a project document, and `--precondition-json` guards against a
|
|
14
|
+
stale edit (below). It prints whether the edit applied, the version it wrote, which operations
|
|
15
|
+
changed nothing (`unchanged_ops`) or were lost to a concurrent change (`lost_ops`), and the document
|
|
16
|
+
token this edit produced, as a `Document token: <token>` line. The ops are a JSON array of this exact `EditOp` shape:
|
|
14
17
|
|
|
15
18
|
```ts
|
|
16
19
|
type EditOp = {
|
|
@@ -28,8 +31,8 @@ type EditOp = {
|
|
|
28
31
|
};
|
|
29
32
|
```
|
|
30
33
|
|
|
31
|
-
An operation takes only these keys. A key it does not declare is refused before the
|
|
32
|
-
|
|
34
|
+
An operation takes only these keys. A key it does not declare is refused before the command sends
|
|
35
|
+
anything, naming the operation and its keys, because every key but `op` is optional: a
|
|
33
36
|
misspelled `with` would otherwise be dropped and the `replace` would delete the text you meant to
|
|
34
37
|
rewrite.
|
|
35
38
|
|
|
@@ -93,7 +96,7 @@ paragraph is stored `\---`, not a rule, so to add a rule, `insert` it beside the
|
|
|
93
96
|
`***`). A CR LF or a lone carriage return in any text you write - an insert's markdown, a `with` in text or in code,
|
|
94
97
|
a suggestion, an upload or a spec - is stored as a line feed, as the browser editor reads both. Use zero-based
|
|
95
98
|
`occurrence` for a
|
|
96
|
-
repeated target; re-read a missing or ambiguous target before retrying. Pass
|
|
99
|
+
repeated target; re-read a missing or ambiguous target before retrying. Pass `--summary` to name the version when recording a decision.
|
|
97
100
|
|
|
98
101
|
**Rewriting several paragraphs is one `replace` per paragraph, then a read-back.** `replace` is inline:
|
|
99
102
|
each `with` is the new text of one paragraph, and outside a code block a `with` that forms two paragraphs is
|
|
@@ -131,17 +134,17 @@ the old block's place is it a `delete` of the old block and then an `insert` of
|
|
|
131
134
|
block before or after it. The delete is what costs the id (below); a typed block keeps its id when the insert
|
|
132
135
|
carries it, which works only in that order, because an insert carrying an id the document still holds is refused.
|
|
133
136
|
The tools refuse a `delete` or `retype` that would take out an `ask` block whose ask is still open, delete-then-insert included:
|
|
134
|
-
reword it with `replace`, relocate it with `move`, or change its question, options, urgency or `multiple` with `
|
|
137
|
+
reword it with `replace`, relocate it with `move`, or change its question, options, urgency or `multiple` with `dispatch edit-ask` if you asked it. A single
|
|
135
138
|
batch that moves an open block out of a container and then deletes the container is refused; make them two calls (the check reads
|
|
136
139
|
the document as it stood before the batch).
|
|
137
140
|
Then read the document back with
|
|
138
|
-
`
|
|
141
|
+
`dispatch doc-read` and read the passage and its neighbours, not a grep for the words you added: an empty
|
|
139
142
|
`with` deletes the matched text on purpose, so a `replace` whose `with` you meant to fill
|
|
140
143
|
empties that paragraph — the block and its id stay, holding nothing — and only a read shows what the document now says.
|
|
141
144
|
|
|
142
145
|
A batch that leaves the document's semantic identity unchanged — including its inline anchor marks, so an edit that only orphans a
|
|
143
146
|
comment or ask anchor still mints its version — mints no version, named or not: the response carries
|
|
144
|
-
`changed: false` with `unchanged_ops` naming each operation that did nothing, and the
|
|
147
|
+
`changed: false` with `unchanged_ops` naming each operation that did nothing, and the output says nothing changed. A `--summary`
|
|
145
148
|
does not force a version for such a batch; `POST /api/v1/artifacts/<id>/versions`, which names the current state on purpose, still does.
|
|
146
149
|
|
|
147
150
|
A `delete` whose `find` is a block's entire text removes the block itself — the bullet, paragraph, or heading, not just its words — and
|
|
@@ -165,11 +168,11 @@ whose anchor lies inside the moved block, or a delete that would leave a typed b
|
|
|
165
168
|
`INVALID_OP` naming the field and the rule.
|
|
166
169
|
|
|
167
170
|
`GET /api/v1/artifacts/<artifact UUID>/blocks` includes a full-state `token` on every block, including
|
|
168
|
-
inline marks. To reject a stale edit, pass
|
|
169
|
-
`{
|
|
170
|
-
`{
|
|
171
|
-
`
|
|
172
|
-
own edit produced: a run of guarded edits passes each
|
|
171
|
+
inline marks. To reject a stale edit, pass `--precondition-json` with exactly one of
|
|
172
|
+
`{"document": "<document token>"}` or
|
|
173
|
+
`{"blocks": [{"id": "<block id>", "token": "<block token>"}]}`. A document token comes from
|
|
174
|
+
`dispatch doc-read` or from the previous `dispatch doc-edit`, whose output carries the token its
|
|
175
|
+
own edit produced: a run of guarded edits passes each output's token to the next one and reads the
|
|
173
176
|
document once, not once per edit. The server resolves the whole batch before
|
|
174
177
|
mutation: a block guard must cover every content block it changes, or Dispatch returns
|
|
175
178
|
`400 INVALID_PRECONDITION` without applying anything. Use a document token for insert and move because they
|
|
@@ -197,6 +200,6 @@ an existing paragraph is the question that should become a decision.
|
|
|
197
200
|
|
|
198
201
|
An ask block has two ids: the block id, shown as `:::ask{#<id> …}` in the rendered document and
|
|
199
202
|
taken bare by `move`/`delete` in `block` (the `block:<id>` form is only for `before`/`after`
|
|
200
|
-
anchors), and the ask id, which `
|
|
201
|
-
`?ask=`), `
|
|
203
|
+
anchors), and the ask id, which `dispatch open-asks`, the dashboard's ask links (`/asks/<id>`,
|
|
204
|
+
`?ask=`), `dispatch read` and `dispatch comment --reply-to-ask` use. They differ;
|
|
202
205
|
`dispatch://KEY/ask/<block-id>` answers `not found`.
|
|
@@ -15,7 +15,7 @@ Dispatch writes its fences that way. An unclosed typed block at document level i
|
|
|
15
15
|
opening line that continues a paragraph instead of standing on its own (indented four or more
|
|
16
16
|
columns under the paragraph's text) is refused, naming the line's number and text - `INVALID_OP` on an edit's `markdown` or `with`, `INVALID_MARKDOWN` on any other write - since it
|
|
17
17
|
would be stored as the paragraph's text; start the block on its own line. An opening written inside
|
|
18
|
-
a line is stored as text, and `
|
|
18
|
+
a line is stored as text, and `dispatch issue` and `dispatch artifact` quote it back as a
|
|
19
19
|
typed-block opening that is text, not a block. For
|
|
20
20
|
a new typed block, omit `#block-id`; Dispatch mints it. When editing an existing typed block, retain
|
|
21
21
|
its id and every rendered attribute. Never copy an existing block's id into new markdown: an id
|
|
@@ -24,9 +24,9 @@ outside the text it replaces is refused naming the id: `INVALID_OP` for an inser
|
|
|
24
24
|
`INVALID_MARKDOWN` for any other write. To rewrite such a block whole, `delete` it and then
|
|
25
25
|
`insert` the new one carrying its id, anchored on the block before or after it, in that order and
|
|
26
26
|
in one batch: an insert carrying an id the document still holds is refused. An `ask` block whose ask
|
|
27
|
-
is still open cannot be rewritten that way:
|
|
27
|
+
is still open cannot be rewritten that way: `dispatch doc-edit` refuses the `delete`, so reword it with
|
|
28
28
|
`replace`, relocate it with `move`, or change its question, options, urgency or `multiple` with
|
|
29
|
-
`
|
|
29
|
+
`dispatch edit-ask` if you asked it ([Editing a document](skill://dispatch/references/document-edits.md)).
|
|
30
30
|
|
|
31
31
|
Use only the type names, content rule, attributes, and enum values returned by the schema. Values are
|
|
32
32
|
quoted: `:::callout{kind="warning" title="Risk"}`. Do not write Pandoc-style `::: {.callout}`, leaf
|
|
@@ -55,67 +55,67 @@ unknown cost before production while keeping today's release possible.
|
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
When a human answers a decision written as an ask block, the answer lives on that ask. Use
|
|
58
|
-
`
|
|
58
|
+
`dispatch resolve-ask` when the decision is resolved without a human response, or preserve the
|
|
59
59
|
human's answer; never rewrite the question into its answer or blank its options. An edit that leaves
|
|
60
60
|
an ask block without a question or with a blank option is rejected with `INVALID_ASK_BLOCK`.
|
|
61
61
|
|
|
62
|
-
When `
|
|
62
|
+
When `dispatch issue` or `dispatch artifact` answers `This spec holds no ask blocks …`, read it as a
|
|
63
63
|
question: either no decision is needed and you say nothing, or you forgot to make it a block. When
|
|
64
64
|
it answers `… typed-block openings in this document are text, not blocks`, the quoted openings are
|
|
65
65
|
blocks stored as prose (inside a line, or a paste with something before every line): fix the markdown
|
|
66
66
|
and upload again; a mention on purpose belongs in code. Neither answer sees a spec wrapped whole in a
|
|
67
67
|
code fence (take the fence off), a malformed opening inside a line (`::ask{`, `:::ask {`: an ask opens
|
|
68
|
-
only as `:::ask{…}` on a line of its own), or any `
|
|
69
|
-
ask, `
|
|
68
|
+
only as `:::ask{…}` on a line of its own), or any `dispatch doc-edit`: after an edit that writes an
|
|
69
|
+
ask, `dispatch doc-read` the section and check it renders as `:::ask{#<id> …}` on its own line.
|
|
70
70
|
|
|
71
71
|
## Comments and suggestions
|
|
72
72
|
|
|
73
|
-
Add feedback with:
|
|
73
|
+
Add feedback with `dispatch comment` (`dispatch comment --help` lists its flags):
|
|
74
74
|
|
|
75
|
-
```
|
|
76
|
-
|
|
75
|
+
```bash
|
|
76
|
+
dispatch comment --issue <KEY> --artifact spec --quote '<the passage>' --body '<the question or note>'
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
-
It
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
for display. Omit both for a floating issue comment. A reply (`
|
|
84
|
-
|
|
79
|
+
It prints the comment's id; a `--reply-to-ask` reply also makes you one of the ask's followers
|
|
80
|
+
(see [Following](skill://dispatch/references/asks.md)).
|
|
81
|
+
`--ref` names the owner (an issue or project-document reference) in place of `--issue`/`--project`.
|
|
82
|
+
`--quote` requires `--artifact`; its anchor is pinned to the containing block while retaining the quote
|
|
83
|
+
for display. Omit both for a floating issue comment. A reply (`--reply-to`/`--reply-to-ask`) takes no
|
|
84
|
+
`--quote`; it belongs to its parent's anchor. Reply to any comment in a thread; the server keeps
|
|
85
85
|
threads flat. A reply to a resolved thread reopens it, except a decided suggestion's: a reply joins
|
|
86
|
-
that thread and the suggestion stays accepted or rejected. Use `
|
|
87
|
-
question asked with `
|
|
86
|
+
that thread and the suggestion stays accepted or rejected. Use `--reply-to-ask` to reply directly under a
|
|
87
|
+
question asked with `dispatch ask`; `--turn` (only with `--reply-to-ask`) says who holds the turn after
|
|
88
88
|
the reply — `agent` for a progress note that keeps the ask waiting on you, `human` (the default) when
|
|
89
89
|
the human needs to act; see "Asking" in `skill://dispatch`. Comments are edited only by their author from the
|
|
90
90
|
dashboard. A delivered `comment.created` event carries the comment `id`; reply to it with
|
|
91
|
-
`
|
|
91
|
+
`dispatch comment --reply-to <id>`.
|
|
92
92
|
|
|
93
93
|
Resolve your own review comment once you have addressed it:
|
|
94
94
|
|
|
95
|
-
```
|
|
96
|
-
|
|
95
|
+
```bash
|
|
96
|
+
dispatch resolve-comment --comment <id>
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
8+ character id prefix is resolved against the owner's comments). It
|
|
101
|
-
say what you did in a `
|
|
99
|
+
`--comment` is the comment id, or a `dispatch://KEY/comment/<id>` or `dispatch://PROJECT/artifact/<slug>/comment/<id>` reference (an
|
|
100
|
+
8+ character id prefix is resolved against the owner's comments). It takes no reason —
|
|
101
|
+
say what you did in a `--reply-to` first if the thread needs it. The server lets any session or human resolve any open comment, so
|
|
102
102
|
resolve only threads you opened or were asked to close; reopening a resolved thread is human-only (from the dashboard), though your
|
|
103
103
|
reply to it reopens it - except a decided suggestion's, which your reply joins and leaves accepted or rejected. A reply to a pending
|
|
104
|
-
suggestion whose text an edit removed lands as well. Asks are closed with `
|
|
104
|
+
suggestion whose text an edit removed lands as well. Asks are closed with `dispatch resolve-ask` instead.
|
|
105
105
|
|
|
106
|
-
An exact replacement for document text is a suggestion (`
|
|
106
|
+
An exact replacement for document text is a suggestion (`dispatch suggest`), never a comment; a
|
|
107
107
|
comment is for a question or a note the human answers in words. A human accepts a suggestion with
|
|
108
108
|
one click and cannot accept a comment, so propose the replacement instead of describing it:
|
|
109
109
|
|
|
110
|
-
```
|
|
111
|
-
|
|
110
|
+
```bash
|
|
111
|
+
dispatch suggest --issue <KEY> --artifact spec --quote '<exact text>' --replace-with '<replacement>'
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
It
|
|
115
|
-
Errors: `TARGET_AMBIGUOUS` (add
|
|
114
|
+
It prints the suggestion's id; `--body` adds a note and `--occurrence` picks a repeated quote. A human accepts or rejects a suggestion.
|
|
115
|
+
Errors: `TARGET_AMBIGUOUS` (add `--occurrence`), `TARGET_NOT_FOUND` (re-read first), `INVALID_ANCHOR`/`ANCHOR_MISSING`/`ANCHOR_ORPHANED`
|
|
116
116
|
(bad, unwritten, or stale quote), `INVALID_MARKDOWN`/`DOC_SCHEMA` (malformed content), `CAP_EXCEEDED`, `ISSUE_CLOSED`.
|
|
117
117
|
Suggest only what the quoted block can hold. The accept, not the suggestion, checks that: an
|
|
118
|
-
accept whose
|
|
118
|
+
accept whose replacement would break an ask block that was readable before it is refused with
|
|
119
119
|
`INVALID_ASK_BLOCK` - a question given a code block, text after an ask's options, a second option
|
|
120
120
|
list, or an emptied question; one that names an id the document holds outside the text it
|
|
121
121
|
replaces, an ask under a held id included, with `INVALID_MARKDOWN`; and one no part of the
|
|
@@ -127,51 +127,54 @@ stays open until someone rejects or replaces it.
|
|
|
127
127
|
|
|
128
128
|
Attach an image, diagram, or local file with:
|
|
129
129
|
|
|
130
|
-
```
|
|
131
|
-
|
|
130
|
+
```bash
|
|
131
|
+
dispatch artifact --issue <KEY> --name diagram.png --path ./diagram.png
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
Or, when the text is already
|
|
134
|
+
Or, when the text is already at hand, post a Markdown document directly:
|
|
135
135
|
|
|
136
|
-
```
|
|
137
|
-
|
|
136
|
+
```bash
|
|
137
|
+
dispatch artifact --issue <KEY> --name load-test-results.md --content-file - <<'EOF'
|
|
138
|
+
# Load test
|
|
139
|
+
...
|
|
140
|
+
EOF
|
|
138
141
|
```
|
|
139
142
|
|
|
140
|
-
Exactly one of
|
|
141
|
-
Exactly one of
|
|
142
|
-
Uploading the same
|
|
143
|
-
with your text. Never do that: the spec is edited in place with `
|
|
144
|
-
artifact by the slug
|
|
143
|
+
Exactly one of `--issue` and `--project` is required. A project upload creates an unlinked project document; it takes no `--artifact`.
|
|
144
|
+
Exactly one of `--path` and `--content` is required. It prints the document's slug and version.
|
|
145
|
+
Uploading the same `--name` creates its next version — so uploading `spec.md` **replaces the issue's own specification**
|
|
146
|
+
with your text. Never do that: the spec is edited in place with `dispatch doc-edit` (see [Editing a document](skill://dispatch/references/document-edits.md)). Address an existing
|
|
147
|
+
artifact by the slug the upload printed or by its filename, and a project document by its artifact id, slug, or filename; the
|
|
145
148
|
slug also arrives on `artifact.created` events. Dispatch suffixes a slug two documents would share, so one document's
|
|
146
149
|
filename can be another's slug (`plan v2` takes `plan-v2`, then a document named `plan-v2` takes `plan-v2-2`): a bare
|
|
147
|
-
|
|
150
|
+
`--artifact` that names both is refused with each one's id, while a `dispatch://` reference's document part is always the slug.
|
|
148
151
|
|
|
149
|
-
A picture meant to be seen inline in a message, comment or ask is not uploaded with `
|
|
150
|
-
path
|
|
152
|
+
A picture meant to be seen inline in a message, comment or ask is not uploaded with `dispatch artifact`: pass its local
|
|
153
|
+
path with that command's `--image`, once per picture (PNG, JPEG, GIF or WebP by its bytes, at most 25 MiB each). Each is uploaded to the issue (a
|
|
151
154
|
project document's project; for a reply to a direct message, your own conversation) and appended after a blank line as
|
|
152
155
|
one `` line, about 60 characters that count toward the body or
|
|
153
|
-
question cap; a
|
|
156
|
+
question cap; a command the lines would carry over the cap is refused with the count before anything uploads, and again,
|
|
154
157
|
with nothing posted, in the rare case where Dispatch gives an upload a longer address than that (a later version of a
|
|
155
158
|
name the owner already holds, or a slug another file took first). The `@vN` pins the version the upload made (the same
|
|
156
159
|
file name again makes the next version). A plain `dispatch://KEY/artifact/<slug>` reference to an image stays a link
|
|
157
160
|
with a thumbnail.
|
|
158
161
|
|
|
159
|
-
`
|
|
160
|
-
10 MiB per read, and its `Pictures:` section names each one shown, in order, and the rest by reference for
|
|
161
|
-
`
|
|
162
|
-
larger one or an SVG is described). A picture in a
|
|
162
|
+
`dispatch read` writes the pictures the messages, asks and comments it shows embed to files, newest first, at most 8 and
|
|
163
|
+
10 MiB per read, prints a `- picture: <path>` line for each, and its `Pictures:` section names each one shown, in order, and the rest by reference for
|
|
164
|
+
`dispatch doc-read`, which shows one picture (at most 3,750,000 bytes, the 5 MB of base64 a model provider takes; a
|
|
165
|
+
larger one or an SVG is described). Open each written file with your file-read tool. A picture in a
|
|
163
166
|
conversation on the Agents page belongs to that conversation, `dispatch://agent/<session id>/artifact/<slug>@vN`, and
|
|
164
|
-
`
|
|
167
|
+
`dispatch doc-read` reads it by that reference alone. On Oh My Pi an Inbox delivery carries its pictures as images; in
|
|
165
168
|
Claude Code it stays text with the reference.
|
|
166
169
|
|
|
167
170
|
A session is shown each picture once. A later read or delivery that embeds a picture you were already shown names it in
|
|
168
171
|
`Pictures:` as `shown earlier this session` and sends no bytes, since every request carries the session's whole history
|
|
169
|
-
and a model provider refuses one over 32 MB; `
|
|
172
|
+
and a model provider refuses one over 32 MB; `dispatch doc-read` always shows the picture, so ask it for one you need
|
|
170
173
|
again, after compaction too.
|
|
171
174
|
|
|
172
175
|
Documents are CommonMark. A bare `<https://example.com|text>` is a CommonMark autolink and is normalised: the angle brackets are
|
|
173
176
|
dropped and the URL keeps `|text`. A backslash-escaped `\<https://example.com|text>` displays as `<https://example.com|text>` in the
|
|
174
|
-
document but comes back re-escaped (`\<`) from `
|
|
177
|
+
document but comes back re-escaped (`\<`) from `dispatch doc-read`. A Slack mrkdwn draft, or any other payload that is not Markdown,
|
|
175
178
|
still belongs inside a fenced code block, where it survives verbatim both ways.
|
|
176
179
|
|
|
177
180
|
A table cell ends at every `|` not written `\|`, inside inline code and links too, so write
|
|
@@ -184,27 +187,27 @@ of bare table rows `TABLE_WIDTH`, which names the cell counts only.
|
|
|
184
187
|
|
|
185
188
|
## Approval requests
|
|
186
189
|
|
|
187
|
-
```
|
|
188
|
-
|
|
190
|
+
```bash
|
|
191
|
+
dispatch request-approval --issue <KEY> --summary '<what the human is approving>'
|
|
189
192
|
```
|
|
190
193
|
|
|
191
|
-
The
|
|
192
|
-
"Approve spec.md (version N)?" followed by
|
|
194
|
+
The command opens an approval ask with the options `Approve` and `Request changes`, its question
|
|
195
|
+
"Approve spec.md (version N)?" followed by the summary, where N is the document's latest version. It
|
|
193
196
|
is refused, with nothing sent, while that version holds a decision block open, and the refusal
|
|
194
|
-
names each block and its ask. An answer or a `
|
|
197
|
+
names each block and its ask. An answer or a `dispatch resolve-ask` closes the ask at once but
|
|
195
198
|
reaches a version only when the document settles, about two seconds later, or with your next
|
|
196
|
-
`
|
|
199
|
+
`dispatch doc-edit`: fold the answer into the text (or, for a waiver, write the human's decision
|
|
197
200
|
in) before you request. A block written in the last few seconds counts as open before Dispatch has
|
|
198
201
|
opened its ask. A document holds one open request. A new version moves it to that version, keeping
|
|
199
202
|
its thread and summary, and leaves it waiting on you, as a human's reply in its thread does; a move
|
|
200
|
-
your own edit made sends you no event, and `
|
|
201
|
-
`Approval: awaiting, waiting on agent`.
|
|
202
|
-
allows: that hands the same request back to the human, reworded first when
|
|
203
|
-
own reply in its thread hands it back too, with no
|
|
204
|
-
you post it with `
|
|
203
|
+
your own edit made sends you no event, and `dispatch read` and `dispatch doc-read` show it as
|
|
204
|
+
`Approval: awaiting, waiting on agent`. Run it again when "Approval of a spec" in `skill://dispatch`
|
|
205
|
+
allows: that hands the same request back to the human, reworded first when `--summary` is new. Your
|
|
206
|
+
own reply in its thread hands it back too, with no command and the summary it already holds, unless
|
|
207
|
+
you post it with `--reply-to-ask` and `--turn agent` (`--reply-to` carries no turn, so its reply takes
|
|
205
208
|
the default, `human`) or a new version has moved the request since your last
|
|
206
|
-
`
|
|
207
|
-
nothing, and
|
|
209
|
+
`dispatch request-approval`. While it waits on the human, the same `--summary` changes
|
|
210
|
+
nothing, and a different `--summary` is refused, since it would rewrite the card they are
|
|
208
211
|
reading. The answer reaches you as `artifact.approved` or `artifact.changes_requested` with the
|
|
209
212
|
pinned `version` and closes the request, so the next call opens a new one; `changes_requested`
|
|
210
213
|
carries the reason, which is your next piece of work. Those reads show the document's approval
|
|
@@ -213,18 +216,18 @@ state; `stale` means it was approved and then edited.
|
|
|
213
216
|
## A document that is reloading
|
|
214
217
|
|
|
215
218
|
These calls can answer `DOC_SERVICE_UNAVAILABLE` (HTTP 503), because each writes a document inside its
|
|
216
|
-
transaction: `
|
|
217
|
-
in a thread whose first comment is anchored; `
|
|
218
|
-
comment; `
|
|
219
|
-
block, `
|
|
220
|
-
uploading a new document, `
|
|
221
|
-
`
|
|
219
|
+
transaction: `dispatch doc-edit`; `dispatch ask` and `dispatch comment` on a quote; a `dispatch comment` reply
|
|
220
|
+
in a thread whose first comment is anchored; `dispatch suggest`; `dispatch resolve-comment` on an anchored
|
|
221
|
+
comment; `dispatch artifact` replacing a document that already exists; and, on an ask that lives in a `:::ask`
|
|
222
|
+
block, `dispatch edit-ask` and `dispatch resolve-ask`, which write that block. Creating an issue with a spec,
|
|
223
|
+
uploading a new document, `dispatch request-approval` and `dispatch message` never answer it, and neither do
|
|
224
|
+
`dispatch edit-ask` and `dispatch resolve-ask` on an ask that has no block. It means that document's live room
|
|
222
225
|
failed and is reloading from its durable copy, so the server refused rather than wait for it; your call wrote
|
|
223
226
|
nothing and the document is intact. Nothing retries it for you: the Dispatch client hands a 503 straight back.
|
|
224
227
|
Wait a few seconds and make the same call again. A second refusal in a row is worth telling your human about,
|
|
225
228
|
with the document's reference.
|
|
226
229
|
|
|
227
|
-
`
|
|
230
|
+
`dispatch doc-read` can answer it too, though it writes nothing: a read never opens a live room, and waits out
|
|
228
231
|
a room that is reloading, so it is refused only when the document's durable copy cannot be read or decoded.
|
|
229
232
|
Retry it the same way.
|
|
230
233
|
|
|
@@ -17,90 +17,60 @@ dashboard from the release. Waiting is safest, but the list above may be stale.
|
|
|
17
17
|
After — write the decision into the document section it concerns: state the problem, and make each
|
|
18
18
|
genuinely different option a button, so the answer stays with the release design:
|
|
19
19
|
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Release notes are unreviewed, and tomorrow's customer demo means the release cannot wait for a later review. How should we proceed? Recommendation: review the notes, then ship, to keep the release complete and reviewed.
|
|
29
|
-
|
|
30
|
-
- Review notes, then ship: Delays release for review but keeps the release complete and reviewed.
|
|
31
|
-
- Ship now: Meets the demo deadline but leaves the release notes unreviewed.
|
|
32
|
-
:::`,
|
|
33
|
-
}],
|
|
34
|
-
})
|
|
20
|
+
```bash
|
|
21
|
+
dispatch doc-edit --issue LEGION-815 --artifact spec --ops-json-file - <<'EOF'
|
|
22
|
+
[{
|
|
23
|
+
"op": "insert",
|
|
24
|
+
"after": "Release requires reviewed operator instructions before deployment.",
|
|
25
|
+
"markdown": ":::ask{#release-gate urgency=\"high\"}\nRelease notes are unreviewed, and tomorrow's customer demo means the release cannot wait for a later review. How should we proceed? Recommendation: review the notes, then ship, to keep the release complete and reviewed.\n\n- Review notes, then ship: Delays release for review but keeps the release complete and reviewed.\n- Ship now: Meets the demo deadline but leaves the release notes unreviewed.\n:::"
|
|
26
|
+
}]
|
|
27
|
+
EOF
|
|
35
28
|
```
|
|
36
29
|
|
|
37
30
|
Before — a progress note that nobody needs, posted where humans look for decisions:
|
|
38
31
|
|
|
39
|
-
```
|
|
40
|
-
|
|
32
|
+
```bash
|
|
33
|
+
dispatch message --issue LEGION-815 --body 'Merged the release PR, moving to docs next.'
|
|
41
34
|
```
|
|
42
35
|
|
|
43
36
|
After — nothing. The merge is visible on the pull request; the docs work shows up as its own deliverable. Post a message only when
|
|
44
37
|
a human must act or a deliverable is theirs to use:
|
|
45
38
|
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
issue: "LEGION-815",
|
|
49
|
-
body: "Release 1.4 is live on the devbox (dispatch://LEGION-815/artifact/release-notes). Nothing needed from you.",
|
|
50
|
-
})
|
|
39
|
+
```bash
|
|
40
|
+
dispatch message --issue LEGION-815 --body 'Release 1.4 is live on the devbox (dispatch://LEGION-815/artifact/release-notes). Nothing needed from you.'
|
|
51
41
|
```
|
|
52
42
|
|
|
53
43
|
Before — a draft the human must read is uploaded as a separate file, the spec only names it, and
|
|
54
44
|
the ask does not point at it, so the reader has to go looking:
|
|
55
45
|
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
"Customers need an update today, but the draft is only in a separate file, so the reader cannot review it in context. How should we proceed? Recommendation: put the draft in the spec before sending it.",
|
|
65
|
-
options: [
|
|
66
|
-
{ label: "Put the draft in the spec", description: "Adds a spec edit before sending but lets the reader review it in context." },
|
|
67
|
-
{ label: "Keep the separate file", description: "Saves the spec edit but leaves the reader to find the draft." },
|
|
68
|
-
],
|
|
69
|
-
})
|
|
46
|
+
```bash
|
|
47
|
+
dispatch artifact --issue OPS-52 --name cu-update-2026-09-15.md --content 'Hi team, ...'
|
|
48
|
+
dispatch doc-edit --issue OPS-52 --artifact spec \
|
|
49
|
+
--ops-json '[{"op":"insert","after":"## Context","markdown":"## Draft (artifact cu-update-2026-09-15.md)"}]'
|
|
50
|
+
dispatch ask --issue OPS-52 \
|
|
51
|
+
--question 'Customers need an update today, but the draft is only in a separate file, so the reader cannot review it in context. How should we proceed? Recommendation: put the draft in the spec before sending it.' \
|
|
52
|
+
--option 'Put the draft in the spec: Adds a spec edit before sending but lets the reader review it in context.' \
|
|
53
|
+
--option 'Keep the separate file: Saves the spec edit but leaves the reader to find the draft.'
|
|
70
54
|
```
|
|
71
55
|
|
|
72
56
|
After — the draft is a section of the spec, and the ask anchors there. Sending it is a to-do only a
|
|
73
57
|
human can complete, so it stays a standalone ask. If it really must be a file, the spec and the ask
|
|
74
|
-
both link the slug
|
|
75
|
-
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
// or, for a real file — the spec links it where the reader needs it, and so does the ask:
|
|
91
|
-
dispatch_doc_edit({ issue: "OPS-52", artifact: "spec", ops: [
|
|
92
|
-
{ op: "insert", after: "## Context", markdown: "## Draft\n\nThe update to send: dispatch://OPS-52/artifact/cu-update-2026-09-15-md" },
|
|
93
|
-
]})
|
|
94
|
-
dispatch_ask({
|
|
95
|
-
issue: "OPS-52",
|
|
96
|
-
question:
|
|
97
|
-
"Customers need an update today, and its reviewed text is linked from the spec. Sending it cannot be recalled. How should we proceed? Recommendation: send the reviewed update.",
|
|
98
|
-
options: [
|
|
99
|
-
{ label: "Send the reviewed update", description: "Delivers the linked update today but makes its text external." },
|
|
100
|
-
{ label: "Hold the update", description: "Avoids sending now but leaves customers without the update." },
|
|
101
|
-
],
|
|
102
|
-
ref: "dispatch://OPS-52/artifact/cu-update-2026-09-15-md",
|
|
103
|
-
})
|
|
58
|
+
both link the slug the upload printed:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
dispatch doc-edit --issue OPS-52 --artifact spec \
|
|
62
|
+
--ops-json '[{"op":"insert","after":"## Context","markdown":"## Draft\n\nHi team, ..."}]'
|
|
63
|
+
dispatch ask --issue OPS-52 --anchor-json '{"artifact":"spec","quote":"Hi team,"}' \
|
|
64
|
+
--question 'Customers need an update today, and the reviewed text is ready in the spec. Sending it cannot be recalled. How should we proceed? Recommendation: send the reviewed update.' \
|
|
65
|
+
--option 'Send the reviewed update: Delivers the update today but makes its text external.' \
|
|
66
|
+
--option 'Hold the update: Avoids sending now but leaves customers without the update.'
|
|
67
|
+
# or, for a real file — the spec links it where the reader needs it, and so does the ask:
|
|
68
|
+
dispatch doc-edit --issue OPS-52 --artifact spec \
|
|
69
|
+
--ops-json '[{"op":"insert","after":"## Context","markdown":"## Draft\n\nThe update to send: dispatch://OPS-52/artifact/cu-update-2026-09-15-md"}]'
|
|
70
|
+
dispatch ask --issue OPS-52 --ref dispatch://OPS-52/artifact/cu-update-2026-09-15-md \
|
|
71
|
+
--question 'Customers need an update today, and its reviewed text is linked from the spec. Sending it cannot be recalled. How should we proceed? Recommendation: send the reviewed update.' \
|
|
72
|
+
--option 'Send the reviewed update: Delivers the linked update today but makes its text external.' \
|
|
73
|
+
--option 'Hold the update: Avoids sending now but leaves customers without the update.'
|
|
104
74
|
```
|
|
105
75
|
|
|
106
76
|
## Naming the mechanism
|
|
@@ -123,7 +93,7 @@ takes today: it calls `register_gate` on its `legion` tool, which posts to the d
|
|
|
123
93
|
`POST /legion/v1/gates/register` route and names the spec document and the version that gate the
|
|
124
94
|
tree. The daemon opens the gate when Dispatch emits `artifact.approved` for that document at that
|
|
125
95
|
version, which Dispatch does when you click Approve on the request the architect opened with
|
|
126
|
-
`
|
|
96
|
+
`dispatch request-approval`. Without `register_gate`, the daemon reads the issue's primary
|
|
127
97
|
document from Dispatch itself and waits for the same event.
|
|
128
98
|
```
|
|
129
99
|
|