@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.
@@ -1,19 +1,21 @@
1
1
  ---
2
2
  name: dispatch
3
- description: "Use before posting a message, a status update, or a periodic status update; before asking a question that references another message, artifact, or eval; before asking a design question or brainstorming a change; and when asking the human a question, updating the spec, commenting on a document, attaching an artifact, or calling a dispatch_* tool."
3
+ description: "Use before posting a message, a status update, or a periodic status update; before asking a question that references another message, artifact, or eval; before asking a design question or brainstorming a change; and when asking the human a question, updating the spec, commenting on a document, attaching an artifact, or running a dispatch command."
4
4
  ---
5
5
 
6
6
  # Dispatch
7
7
 
8
8
  Dispatch is your issue's or project document's living spec, asks, comments, and artifacts — a high-signal record for the humans who
9
9
  decide, never a log of your work. The transcript is your scratch pad; progress and status stay there. Anything meant for a human
10
- goes through a `dispatch_*` tool.
10
+ goes through the `dispatch` command, which runs in your shell: `dispatch --help` lists the commands, and `dispatch <command> --help`
11
+ each one's flags and an example. A field is a flag (`--issue LEGION-3`); long text goes on stdin with `--<field>-file -` and a
12
+ quoted here-document (`<<'EOF'` … `EOF`), and an object or list field takes JSON (`--ops-json`). What it prints is the result; it
13
+ exits 0 when it wrote or read, 1 when Dispatch refused, and 2 on a usage error.
11
14
 
12
15
  The server enforces high signal: an ask question is at most 800 characters with at most eight options; comment and message bodies are at
13
16
  most 2,000 characters; a markdown document is at most 1 MiB and any other file at most 25 MiB. It refuses over-limit input with the number
14
- to trim (`question is 50 characters over the 800-character limit (850/800)`); it never truncates it. A tool call with several problems is
15
- refused once, every problem listed (`<tool> was not called: N problems`), so one corrected call lands. GitHub threads and markers no
16
- longer exist.
17
+ to trim (`question is 50 characters over the 800-character limit (850/800)`); it never truncates it. A command with several problems is
18
+ refused once, every problem listed (`dispatch <command> was not called: N problems`), so one corrected call lands.
17
19
 
18
20
  ## Where the detail lives
19
21
 
@@ -23,14 +25,14 @@ after `skill://dispatch/` is relative to this skill's base directory.
23
25
 
24
26
  | When you are about to | Read |
25
27
  | --- | --- |
26
- | call `dispatch_doc_edit`: rewrite a paragraph, insert or move a block, change a table's cells, rows or columns | [Editing a document](skill://dispatch/references/document-edits.md) |
28
+ | run `dispatch doc-edit`: rewrite a paragraph, insert or move a block, change a table's cells, rows or columns | [Editing a document](skill://dispatch/references/document-edits.md) |
27
29
  | write a typed block (an `:::ask`, a callout), comment on or suggest a change to a document, upload an artifact, or retry after `DOC_SERVICE_UNAVAILABLE` | [Documents](skill://dispatch/references/documents.md) |
28
30
  | choose your next issue, claim one, move its status or priority, reorder a board, or audit a project's backlog | [Working an issue](skill://dispatch/references/issues.md) |
29
31
  | find who answers an ask, edit, retract or resolve one, reply with the turn, or follow a thread | [Asks after they open](skill://dispatch/references/asks.md) |
30
32
  | catch up after a restart, trace what cites a node, or write a `dispatch://` reference | [Reading back](skill://dispatch/references/reading.md) |
31
33
  | answer a BTW, Aside or Steer frame, or a message from the Agents page | [Targeted and direct messages](skill://dispatch/references/messages.md) |
32
34
  | see a worked ask, a reply that names its mechanism, a message not to send, and where a draft goes | [Before and after](skill://dispatch/references/examples.md) |
33
- | set up a token, or call a route the tools do not cover | [Authentication and the HTTP API](skill://dispatch/references/api.md) |
35
+ | set up a token, or call a route the commands do not cover | [Authentication and the HTTP API](skill://dispatch/references/api.md) |
34
36
 
35
37
  ## Design changes are brainstormed here
36
38
 
@@ -47,7 +49,7 @@ not share this session's vocabulary, and is often on a phone. Write for that per
47
49
  - Plain English, full sentences, one idea per sentence. Never repo shorthand or nouns you coined:
48
50
  not "fix 8c", "READY-target", "PR B", "spec@v3", "the pair", "the packet" — say what the thing is.
49
51
  - A real name is not a coined noun: name a product, a tool and its operation
50
- (`dispatch_request_approval`), an event type (`artifact.approved`), a route, a setting or a
52
+ (`dispatch request-approval`), an event type (`artifact.approved`), a route, a setting or a
51
53
  status exactly, and say how it works in a clause ("the daemon opens the gate when Dispatch emits
52
54
  `artifact.approved` for that document at that version"), never with a vague verb such as
53
55
  "notice", "accept", "tell" or "pick up" ([before and after](skill://dispatch/references/examples.md#naming-the-mechanism)).
@@ -82,7 +84,7 @@ a new version that keeps the human's own text, never a second "spec" artifact be
82
84
  says. Because it is an ask, it reaches the human's Inbox, and the answer lands next to its context.
83
85
  - **A settled point records the person's own words and the date**, quoted, so no reader mistakes
84
86
  it for your inference; an answer that is only a chosen option is recorded in the form
85
- `<name> chose "Commit author" on the question below (<date>)`, naming them from `dispatch_whoami`
87
+ `<name> chose "Commit author" on the question below (<date>)`, naming them from `dispatch whoami`
86
88
  or the conversation, or "the person" when the token names no owner — never a name you were not
87
89
  given. A point you inferred says so, with the reasoning — a security concern, a restriction, or
88
90
  a cost nobody measured is exactly this, not a settled constraint; during a live brainstorming
@@ -117,20 +119,20 @@ A decision a human must make is an `:::ask` block at the end of the section that
117
119
  shaped as [Writing for the human](#writing-for-the-human) says for a question, with what
118
120
  constrains the answer split into measured, known and unknown. It asks how to solve the problem,
119
121
  never whether to apply a change already chosen. Never gather decisions into a list, at the top,
120
- at the bottom or in an "open questions" section, and never ask one as a standalone `dispatch_ask`
122
+ at the bottom or in an "open questions" section, and never ask one as a standalone `dispatch ask`
121
123
  that points at the spec.
122
124
 
123
125
  The block is what reaches the human's Inbox. A question phrased as prose in the spec reaches
124
126
  nobody. A spec with no ask blocks is fine only when the issue genuinely needs no human decision.
125
- What `dispatch_issue` and `dispatch_artifact` answer about a spec's blocks, and how to check an edit
127
+ What `dispatch issue` and `dispatch artifact` answer about a spec's blocks, and how to check an edit
126
128
  wrote one, is in [Typed blocks](skill://dispatch/references/documents.md#typed-blocks).
127
129
 
128
130
  **Wrong:** a **Decisions needed** list at the top of the spec with three bullets.
129
131
  **Right:** each decision an `:::ask{#slug}` block at the end of the design section that discusses
130
132
  it, with 2–4 options, a recommendation, and surrounding prose that explains the trade-off.
131
133
 
132
- A spec that already has the pile is repaired with `move`, not rewritten: `dispatch_doc_edit` with
133
- `{ op: "move", block: "<block-uuid>", after: "<the last sentence of the section that discusses it>" }` relocates
134
+ A spec that already has the pile is repaired with `move`, not rewritten: `dispatch doc-edit` with
135
+ `--ops-json '[{"op":"move","block":"<block-uuid>","after":"<the last sentence of the section that discusses it>"}]'` relocates
134
136
  the block and keeps its ask, its answer and its followers; the context paragraphs that were lifted
135
137
  out of Design move the same way, and the emptied section is deleted. An ask block has two ids that
136
138
  differ; [Editing a document](skill://dispatch/references/document-edits.md) says which.
@@ -140,38 +142,32 @@ See [Typed blocks](#typed-blocks) for the syntax and [Before you ask](#before-yo
140
142
 
141
143
  ## Your owner
142
144
 
143
- Every session works on an issue or project document. Legion pre-fills `issue` from `LEGION_ISSUE`: use a native issue key such as
145
+ Every session works on an issue or project document. Legion pre-fills `--issue` from `LEGION_ISSUE`: use a native issue key such as
144
146
  `LEGION-3`, an external `owner/repo#n` reference, or a bare positive number (resolved against the cwd repository). Otherwise pass
145
- exactly one owner to every owner-scoped tool: `issue` for an issue, or `project` and `artifact` for an unlinked project document (see
147
+ exactly one owner to every owner-scoped command: `--issue` for an issue, or `--project` and `--artifact` for an unlinked project document (see
146
148
  [Reference forms](skill://dispatch/references/reading.md) for the resulting ref shape). An external issue reference addresses the existing Dispatch issue linked to
147
- that GitHub issue or pull request. Only `dispatch_issue` with `external` creates a native issue; if no issue is linked, call
148
- `dispatch_issue({ external: "owner/repo#n", project: "<project>", title: "<title>" })` before addressing it.
149
+ that GitHub issue or pull request. Only `dispatch issue` with `--external` creates a native issue; if no issue is linked, run
150
+ `dispatch issue --external owner/repo#n --project <project> --title '<title>'` before addressing it.
149
151
 
150
- Create an issue for newly tracked work with:
151
- ```ts
152
- dispatch_issue({ project, title, parent?, external?, spec?, force?, labels?: string[], priority?: 0 | 1 | 2 | 3, assignee?: string })
153
- ```
154
- `labels` are optional initial labels: Dispatch trims them, preserves their case, and removes case-insensitive duplicates. `priority` is yours on creation too — see [Priority is yours to set](skill://dispatch/references/issues.md). Set `assignee` (the email of a person who has signed in to Dispatch) only when the human said who owns the work; otherwise the default above applies, so a child inherits its parent's assignee. It returns
155
- `details` `{ issue }`; creating an issue does not subscribe you to it (see [Following](#following)). Use `dispatch_issue` only to create an issue; never use it to park a question. When `spec` is supplied,
152
+ Create an issue for newly tracked work with `dispatch issue --project <KEY> --title '<title>'` (`dispatch issue --help` lists `--parent`, `--spec-file`, `--label`, `--priority` and the rest).
153
+ Labels are optional: Dispatch trims them, preserves their case, and removes case-insensitive duplicates. `--priority` is yours on creation too — see [Priority is yours to set](skill://dispatch/references/issues.md). Set `--assignee` (the email of a person who has signed in to Dispatch) only when the human said who owns the work; otherwise the default above applies, so a child inherits its parent's assignee. It prints
154
+ the new key; creating an issue does not subscribe you to it (see [Following](#following)). Use `dispatch issue` only to create an issue; never use it to park a question. When you pass a spec,
156
155
  follow [Writing a spec](#writing-a-spec).
157
156
 
158
157
  ## Search first
159
158
 
160
159
  `skill://dispatch-first`, which every session with Dispatch carries, says how to search before
161
160
  you plan, start a design document, file an issue, ask, post a finding or start work, and what to
162
- do with each hit. What it leaves out:
163
- ```ts
164
- dispatch_search({ query, project?, limit?, offset? })
165
- ```
161
+ do with each hit. What it leaves out (`dispatch search --help` has `--project`, `--limit` and `--offset`):
166
162
  Websearch syntax applies: `"merge queue"`, `-daemon`, `OR`. Issues, documents, asks, comments, and messages rank separately; the
167
163
  merged page orders each kind's best in turn — issue, document, ask, comment, message — and a bare issue key ranks first. Each
168
164
  kind's list also merges a query embedding's meaning match with keyword hits (LEGION-549): a paraphrase with no shared word can
169
- still match; without an embedder, it says so and ranks by keyword alone. A page holds `limit` hits (20 default, 50 max); the first
165
+ still match; without an embedder, it says so and ranks by keyword alone. A page holds `--limit` hits (20 default, 50 max); the first
170
166
  line gives the total (`showing 1-20 of 312`); see [Search paging](skill://dispatch/references/issues.md#search-paging) for more.
171
167
  Queries over 1,000 characters are refused: use the few words `skill://dispatch-first` names, never a pasted passage. Issue hits
172
168
  start with the issue key; document hits start with `dispatch://PROJECT/artifact/<slug>`, then the link. Cite the hit (`dispatch://KEY` or the doc ref) or say "no prior issue".
173
169
 
174
- `dispatch_issue` refuses a title that near-duplicates an issue in the same project and returns the candidates (`POSSIBLE_DUPLICATE`). Read them; reference the existing issue, or repeat the call with `force: true` when it is genuinely new work.
170
+ `dispatch issue` refuses a title that near-duplicates an issue in the same project and prints the candidates (`POSSIBLE_DUPLICATE`). Read them; reference the existing issue, or run it again with `--force` when it is genuinely new work.
175
171
  The check compares title words only (shared stemmed terms), never meaning: "four tests that fail a
176
172
  merge" pairs with "four CI gates that cannot fail a merge". So when you force past a candidate, give
177
173
  the new issue a title that names what differs where you can, and open its spec with the
@@ -189,9 +185,9 @@ is the tell.
189
185
 
190
186
  Before you start implementing an issue, claim it, and release the claim when you stop: finished,
191
187
  handing over, or moving to something else. Closing the issue releases it for you.
192
- ```ts
193
- dispatch_claim({ issue: "LEGION-234" }) // I am implementing this
194
- dispatch_claim({ issue: "LEGION-234", release: true }) // I have stopped; it is free
188
+ ```bash
189
+ dispatch claim --issue LEGION-234 # I am implementing this
190
+ dispatch claim --issue LEGION-234 --release # I have stopped; it is free
195
191
  ```
196
192
  A claim is intent to implement: reading, commenting, asking, or gating a pull request claims
197
193
  nothing. `409 ISSUE_CLAIMED` means someone else holds the issue; never work it in parallel. What
@@ -201,10 +197,10 @@ each refusal means, and when a claim is yours to take, is in
201
197
  ## Issue status is yours to move
202
198
 
203
199
  Claiming and moving the status are two actions, and you do both: when you start,
204
- `dispatch_claim({ issue })` and `dispatch_issue_update({ issue, status: "in_progress" })`. Outside
200
+ `dispatch claim --issue <KEY>` and `dispatch issue-update --issue <KEY> --status in_progress`. Outside
205
201
  Legion the session doing the work moves its issue and the children it owns: `in_progress` when
206
202
  implementation starts, `testing` while the change is proven on a production-like surface,
207
- `needs_review` when its pull request waits on the merge queue, and `done`, with a `reason`, once
203
+ `needs_review` when its pull request waits on the merge queue, and `done`, with a `--reason`, once
208
204
  the change has been driven in production. Never write the status of an issue that carries the
209
205
  `legion` label, or of any issue under one: the Legion daemon writes those. Closing, clearing a
210
206
  field, and choosing your next issue are in [Working an issue](skill://dispatch/references/issues.md).
@@ -216,11 +212,11 @@ block in that document ([Decision blocks](#decision-blocks)), whatever phase the
216
212
  whether or not the document was approved: a block in an approved document makes the approval
217
213
  stale, which is right. A Legion phase worker sends such a decision to its architect, which writes
218
214
  the block (`skill://legion-worker`). A to-do or permission only a human can give, or a decision
219
- with no document to live in, is a `dispatch_ask`. The gates below apply to both.
215
+ with no document to live in, is a `dispatch ask`. The gates below apply to both.
220
216
 
221
217
  ### Before you ask
222
218
 
223
- Every `dispatch_ask` passes four gates first:
219
+ Every `dispatch ask` passes four gates first:
224
220
 
225
221
  1. **Does it need his authority, taste, or risk appetite?** This is the bar for a decision
226
222
  written as an `:::ask` block in context ([Decision blocks](#decision-blocks)). Technical
@@ -228,7 +224,7 @@ Every `dispatch_ask` passes four gates first:
228
224
  internals are your lane's to decide where the work happens, in the plan or the code, not in the
229
225
  spec. A contract between two lanes is settled by those two lanes over Envoy, and you open no ask
230
226
  for it. A halt condition (a change to IAM, deletion or exposure of production data, anything
231
- that reaches a customer) passes this gate: it is your own `dispatch_ask` to the human on your own
227
+ that reaches a customer) passes this gate: it is your own `dispatch ask` to the human on your own
232
228
  issue.
233
229
  2. **Is there genuine uncertainty, and have you measured what you can?** If there is none, it is
234
230
  a plan you execute. The one legitimate ask without uncertainty is permission for an action
@@ -245,7 +241,7 @@ Every `dispatch_ask` passes four gates first:
245
241
  Report what the measurement could **not** establish, with its own control: "I found no
246
242
  evidence" and "there is no evidence to find" read alike and mean opposite things, and a
247
243
  control that shares the query's blind spot proves neither. Before you say you are waiting on
248
- him, run `dispatch_open_asks` (below) — and the test for a new ask is not whether you asked on
244
+ him, run `dispatch open-asks` (below) — and the test for a new ask is not whether you asked on
249
245
  this issue before, but whether it asks him to re-report something he has already answered.
250
246
  3. **Can someone who has not read the code answer it on a phone?** Write it as
251
247
  [Writing for the human](#writing-for-the-human) says — who can do what today and what changes
@@ -268,35 +264,27 @@ Every `dispatch_ask` passes four gates first:
268
264
  Nobody audits or retracts another session's asks: passing every gate, and carrying the content
269
265
  instead of pointing at another message in prose (below), is the asking session's own check.
270
266
 
271
- Open a standalone ask with:
272
- ```ts
273
- dispatch_ask({
274
- issue?,
275
- project?,
276
- artifact?,
277
- ref?,
278
- question,
279
- options?: { label, description? }[],
280
- multiple?,
281
- urgency?,
282
- anchor?: { artifact, quote, occurrence? },
283
- })
267
+ Open a standalone ask with `dispatch ask` (`dispatch ask --help` lists its flags), one `--option` per choice:
268
+ ```bash
269
+ dispatch ask --issue <KEY> --question-file - --option 'Label: what it costs' --option 'Other: what it costs' <<'EOF'
270
+ <the problem, what constrains the answer, and your recommendation with its reason>
271
+ EOF
284
272
  ```
285
- It returns `details` `{ issue, ask, follows: { ask } }` for an issue or `{ project, artifact, document, ask, follows: { ask } }` for a project document: you follow the ask you opened (see [Following](#following)).
273
+ It prints the ask's id; you follow the ask you opened (see [Following](#following)).
286
274
 
287
- References belong in the question text; `ref` is sugar that appends its `dispatch://` value to the question as a rendered link.
275
+ References belong in the question text; `--ref` is sugar that appends its `dispatch://` value to the question as a rendered link.
288
276
 
289
277
  An ask is read on a phone by someone who has not read the code. Write its question and options as
290
278
  [Writing for the human](#writing-for-the-human) says, and apply its phone test before posting.
291
279
  Never lead a question with a file path, line number, sequence number, document version or role token: its own text explains the problem.
292
- Evidence below it may cite one where the reader checks it, or anchor the ask to the passage with `anchor: { artifact, quote, occurrence? }`; `occurrence` is zero-based and selects a repeated quote.
280
+ Evidence below it may cite one where the reader checks it, or anchor the ask to the passage with `--anchor-json '{"artifact":"spec","quote":"…"}'`; an optional `occurrence` is zero-based and selects a repeated quote.
293
281
  A quote anchor is pinned to its lowest complete
294
282
  containing block while retaining its quote as display text, so rewording the passage keeps it
295
283
  attached; a quote spanning top-level blocks, and existing anchors without a block, stay readable
296
284
  against their original document version if their quote disappears.
297
285
 
298
286
  An ask must be answerable from its own text and its anchor alone. Anchor a to-do about a document
299
- passage with `anchor`. Follow up on an ask or comment with `dispatch_comment`; cite anything else
287
+ passage with `--anchor-json`. Follow up on an ask or comment with `dispatch comment`; cite anything else
300
288
  with a `dispatch://` reference (see [References](#references)). Never write "see above", "the
301
289
  message above", or "as attached".
302
290
 
@@ -321,20 +309,20 @@ they must read to decide belongs in the spec in the first place — see [Artifac
321
309
 
322
310
  ### When you need a human
323
311
 
324
- Before saying you are waiting for human input, call `dispatch_open_asks`. With no arguments it lists this session's active asks across open issues and project documents, including whether the human or agent owes the next reply. With `dispatch_open_asks({ project })` it lists every open ask in that project — on its issues and on its documents, whoever authored them — which is how you see what a whole project is waiting on rather than just your own asks.
312
+ Before saying you are waiting for human input, run `dispatch open-asks`. With no flags it lists this session's active asks across open issues and project documents, including whether the human or agent owes the next reply. With `dispatch open-asks --project <KEY>` it lists every open ask in that project — on its issues and on its documents, whoever authored them — which is how you see what a whole project is waiting on rather than just your own asks.
325
313
 
326
314
  **Unsettled product shape needs a decision before implementation.** When a page, navigation entry, table key, customer-scoping rule, or persisted sidecar would set product shape that the human has not already settled, write a decision block in the document that records the work before the first implementation commit; in a Legion tree the architect writes it, and a phase worker sends the decision to its architect. A lane's schema decision or a contract two lanes agree does not settle product shape. This does not turn a user-specified decision or routine implementation into an approval request. A control or behaviour the human asked for in words is settled by those words, together with every choice inside it that his words do not make (where it sits, its defaults, …
327
315
 
328
316
  **Anything you are blocked on a human for is visible in Dispatch.** An agent waits on a human only
329
317
  through an open ask. A to-do, permission, credential or grant renewal, setting only they can
330
- change, review click, or conflict between two of their own rules is a `dispatch_ask` the moment you
318
+ change, review click, or conflict between two of their own rules is a `dispatch ask` the moment you
331
319
  know. Start with the problem and why it matters, then the constraints, options and their costs, and
332
320
  your recommendation. For an action only the human can perform, state what it changes and risks;
333
321
  never make the action itself the question (a design decision is a decision block, as above). Never
334
322
  write a human to-do only into a spec, a comment reply, a message, or a pull-request body: nothing
335
323
  in those paths reaches the human's Inbox, and a human who is not reading the document does not
336
324
  know they are the blocker. Before asking, try to remove the step: a value already on the machine, a
337
- permission you already hold, an API that replaces the click. One ask per item, `urgency: "high"`
325
+ permission you already hold, an API that replaces the click. One ask per item, `--urgency high`
338
326
  when work is stopped on it; while it is open, keep working on everything that is not.
339
327
 
340
328
  Once an ask is open (who answers it, handing a human a to-do, editing, retracting or resolving it,
@@ -345,32 +333,32 @@ answering a clarification, whose turn a reply gives, an answer the human changes
345
333
 
346
334
  Attach every architectural issue to the components it changes. Attach the root before decomposing it: children inherit the root's effective attachment unless they deliberately set their own, so one root attachment classifies the whole tree.
347
335
 
348
- ```
349
- dispatch_issue({ project: "CORE", title: "...", components: { mode: "explicit", ids: ["dispatch-server", "web"] } })
350
- dispatch_issue_update({ issue: "CORE-12", components: { mode: "explicit", ids: ["web"] } }) // this issue's own set, replacing what it inherited
351
- dispatch_issue_update({ issue: "CORE-13", components: { mode: "none", reason: "hiring, not code" } })
352
- dispatch_issue_update({ issue: "CORE-14", components: { mode: "inherit" } }) // back to the parent chain's attachment
336
+ ```bash
337
+ dispatch issue --project CORE --title '...' --components-json '{"mode":"explicit","ids":["dispatch-server","web"]}'
338
+ dispatch issue-update --issue CORE-12 --components-json '{"mode":"explicit","ids":["web"]}' # its own set, replacing what it inherited
339
+ dispatch issue-update --issue CORE-13 --components-json '{"mode":"none","reason":"hiring, not code"}'
340
+ dispatch issue-update --issue CORE-14 --components-json '{"mode":"inherit"}' # back to the parent chain's attachment
353
341
  ```
354
342
 
355
- No native Dispatch tool lists component ids. Read the configured model with `GET /api/v1/projects/{key}/architecture` and use each non-external `components[].id`; when its source repository is your checkout, those ids are the file names under `.dispatch/architecture/` (`web.md` → `web`). An id absent from the model, or an `external` component, is refused with `COMPONENTS_INPUT`.
343
+ No `dispatch` command lists component ids. Read the configured model with `GET /api/v1/projects/{key}/architecture` and use each non-external `components[].id`; when its source repository is your checkout, those ids are the file names under `.dispatch/architecture/` (`web.md` → `web`). An id absent from the model, or an `external` component, is refused with `COMPONENTS_INPUT`.
356
344
 
357
- Use `mode: "none"` with a concrete reason only when the work is genuinely non-architectural (for example hiring, process, or operations work). A closed issue can be classified without reopening. Change code and its architecture description (`.dispatch/architecture/<id>.md`) in the same review. When the model you read is behind your checkout, `dispatch_architecture_sync` imports it now ([Syncing a project's architecture model](skill://dispatch/references/issues.md)).
345
+ Use `"mode":"none"` with a concrete reason only when the work is genuinely non-architectural (for example hiring, process, or operations work). A closed issue can be classified without reopening. Change code and its architecture description (`.dispatch/architecture/<id>.md`) in the same review. When the model you read is behind your checkout, `dispatch architecture-sync` imports it now ([Syncing a project's architecture model](skill://dispatch/references/issues.md)).
358
346
 
359
347
  ## Close what you opened
360
348
 
361
349
  An ask you opened is yours until it is answered or you resolve it. When the answer arrives some
362
350
  other way — the human said it live, a later comment settled it, or the question became moot because the
363
- design moved — resolve it yourself with `dispatch_resolve_ask` in the same turn you learn that.
351
+ design moved — resolve it yourself with `dispatch resolve-ask` in the same turn you learn that.
364
352
  Never leave it for the human to clear.
365
353
 
366
354
  Every later write on the issue answers `You still have an open ask on …` and names it. Treat that
367
355
  as the checklist: if it is still needed, leave it; if it was answered elsewhere, resolve it with
368
356
  the resolving fact as the reason. Before posting a new ask, inspect your open ones. If the new ask
369
- supersedes one, retract the old one with `dispatch_resolve_ask` and kind `retracted` in the same
357
+ supersedes one, retract the old one with `dispatch resolve-ask --kind retracted` in the same
370
358
  turn.
371
359
 
372
360
  See [Following](#following) for why you receive what happens to asks you open and
373
- [What comes back](skill://dispatch/references/reading.md) for finding them again with `dispatch_open_asks`.
361
+ [What comes back](skill://dispatch/references/reading.md) for finding them again with `dispatch open-asks`.
374
362
 
375
363
  ## Approval of a spec
376
364
 
@@ -387,40 +375,38 @@ hold (`skill://legion-architect`).
387
375
  When all three hold, request it in the pass that finishes the spec: a design waiting with nothing in
388
376
  the human's Inbox waits on nobody. When a human asks for approval while a block is open, do not
389
377
  request it and do not hold it silently: name each open block and ask them to answer it or waive
390
- it. For a waiver, close the block with `dispatch_resolve_ask` (`kind: "resolved"`, their words as
391
- `reason`), then write their decision into the text in their words and request.
392
- `dispatch_request_approval` refuses while any block is open.
378
+ it. For a waiver, close the block with `dispatch resolve-ask --kind resolved` (their words as
379
+ `--reason`), then write their decision into the text in their words and request.
380
+ `dispatch request-approval` refuses while any block is open.
393
381
 
394
382
  An approval request carries nothing new: a point the human has not agreed to gets its own decision
395
383
  block first, with your recommendation, or, when it is a choice you can make yourself
396
384
  ([Before you ask](#before-you-ask), gate 1) and the human will not want a say in it, comes out of
397
385
  the spec and is made where the work happens; an inference you cannot defend in a decision block
398
- comes out of the spec. `summary` says in one to three sentences what the human is approving and
386
+ comes out of the spec. `--summary` says in one to three sentences what the human is approving and
399
387
  nothing else: no commentary on itself or on the conversation, and no open question. The Inbox
400
388
  shows it after "Approve spec.md (version N)?". Never write "Approve" options into an ordinary
401
- `dispatch_ask`; only humans approve. The call, its result and its answer:
389
+ `dispatch ask`; only humans approve. The call, its result and its answer:
402
390
  [Approval requests](skill://dispatch/references/documents.md#approval-requests).
403
391
 
404
392
  ## The Spec
405
393
 
406
394
  The spec holds the design and the decisions that shaped it, written as [Writing a spec](#writing-a-spec)
407
395
  says, which also lists what it never carries. It changes when the conversation changes it, and
408
- every version that records a decision is named with `dispatch_doc_edit`'s `summary`.
396
+ every version that records a decision is named with `dispatch doc-edit --summary`.
409
397
 
410
- Read the current document before changing it:
411
-
412
- ```ts
413
- dispatch_doc_read({ issue?, project?, artifact?, version?, ref? })
414
- ```
415
- It returns live or versioned markdown with open marks. A live read ends with a document token; `issue` with an omitted
416
- `artifact` reads the issue specification; a project needs `artifact`; and a `dispatch://PROJECT/artifact/<document-ref>`
398
+ Read the current document before changing it with `dispatch doc-read` (`--issue`, or `--project`
399
+ and `--artifact`, an optional `--version`, or `--ref`).
400
+ It prints live or versioned markdown with open marks. A live read ends with a document token; `--issue` with no
401
+ `--artifact` reads the issue specification; a project needs `--artifact`; and a `dispatch://PROJECT/artifact/<document-ref>`
417
402
  ref supplies both, where `document-ref` is the slug (an id or a filename resolves when no document has that slug). A file
418
- `dispatch_artifact` uploaded reads its text at the latest or named version, or a description when it is not UTF-8 text.
419
- A picture (PNG, JPEG, GIF or WebP under 3,750,000 bytes, 5 MB of base64) comes back as an image you see, with its name, type, size and version;
420
- that includes `dispatch://agent/<session id>/artifact/<slug>@vN`, one a person sent you on the Agents page. A session is shown each picture once,
421
- since the provider refuses a request over 32 MB: later reads and deliveries name it, and `dispatch_doc_read` shows it again (after compaction too).
403
+ `dispatch artifact` uploaded reads its text at the latest or named version, or a description when it is not UTF-8 text.
404
+ A picture (PNG, JPEG, GIF or WebP under 3,750,000 bytes, 5 MB of base64) is written to a file, and the command prints a
405
+ `- picture: <path>` line with its type and size: open it with your file-read tool. That includes
406
+ `dispatch://agent/<session id>/artifact/<slug>@vN`, one a person sent you on the Agents page. A session is shown each picture once,
407
+ since the provider refuses a request over 32 MB: later reads and deliveries name it, and `dispatch doc-read` shows it again (after compaction too).
422
408
 
423
- Editing one is [Editing a document](skill://dispatch/references/document-edits.md): the shape of `dispatch_doc_edit`,
409
+ Editing one is [Editing a document](skill://dispatch/references/document-edits.md): the shape of `dispatch doc-edit`,
424
410
  how to quote the text you mean, one `replace` per paragraph, preconditions against a stale edit, and
425
411
  what each operation costs a block's id and its anchors.
426
412
 
@@ -435,12 +421,12 @@ as [Documents](skill://dispatch/references/documents.md) says.
435
421
  a summary — goes in the spec as a section: the spec is the one document they open. A separate
436
422
  artifact is for a real file: something sent as-is, a long report, a binary, a screenshot.
437
423
 
438
- When you do upload one, the spec links it as `dispatch://KEY/artifact/<slug>` (the `slug` from the
439
- upload result; it renders as a link) at the place the reader needs it, and the ask that needs the
424
+ When you do upload one, the spec links it as `dispatch://KEY/artifact/<slug>` (the slug the
425
+ upload prints; it renders as a link) at the place the reader needs it, and the ask that needs the
440
426
  decision carries the same reference. A heading or a sentence naming the filename is not a
441
427
  reference.
442
428
 
443
- Uploading one (`dispatch_artifact`, its slugs, versions and Markdown rules) is in
429
+ Uploading one (`dispatch artifact`, its slugs, versions and Markdown rules) is in
444
430
  [Documents](skill://dispatch/references/documents.md).
445
431
 
446
432
  ## Structure over stream
@@ -454,12 +440,12 @@ Dispatch is a structured workspace, never a message stream. The structure IS the
454
440
  document, or an artifact on the issue it belongs to.
455
441
  - **Content lives in documents; decisions live in asks; messages only announce.** A draft the
456
442
  human must read goes in the issue's spec (see [Artifacts](#artifacts)), which the dashboard renders with versions and margins; the
457
- ask that needs their word references it (`ref`, or the `dispatch://` link inline) instead of
443
+ ask that needs their word references it (`--ref`, or the `dispatch://` link inline) instead of
458
444
  restating it. A message never carries a body a human has to scroll.
459
445
  - **Never split one deliverable across a message + an ask that points at it.** Ask the question
460
446
  with the document reference in the question text; the reader lands on the content in one click.
461
447
 
462
- The tool result answers your third consecutive message on an issue with no human reply with
448
+ The command answers your third consecutive message on an issue with no human reply with
463
449
  `You've sent N messages …`. That is the ledger pattern being named; stop and either wait or ask
464
450
  once.
465
451
 
@@ -470,15 +456,17 @@ change a human must know about now: a deliverable landed. Nothing else — no pr
470
456
  "starting X", no "still working", no restating the spec, no status on a timer. Your transcript is where work is narrated; the
471
457
  pull request is where it is summarised. One message that a human reads beats ten that train them to skip you.
472
458
 
473
- ```ts
474
- dispatch_message({ issue, body })
459
+ ```bash
460
+ dispatch message --issue <KEY> --body-file - <<'EOF'
461
+ <what landed, with its link>
462
+ EOF
475
463
  ```
476
464
 
477
- It returns `details` `{ issue, message }`. `body` is capped at 2,000 characters. A message is not a decision
478
- (a decision block, or `dispatch_ask` for a human to-do) or document feedback (`dispatch_comment`), and it does not wake anyone unless the issue is routed.
465
+ The body is capped at 2,000 characters. A message is not a decision
466
+ (a decision block, or `dispatch ask` for a human to-do) or document feedback (`dispatch comment`), and it does not wake anyone unless the issue is routed.
479
467
 
480
- **Pictures.** To show a screenshot inline, pass its path in `images` (`dispatch_message`, `dispatch_comment`, `dispatch_ask`);
481
- `dispatch_read` hands you the pictures what it shows embeds. Both are in [Documents](skill://dispatch/references/documents.md), "Artifacts".
468
+ **Pictures.** To show a screenshot inline, pass its path with `--image` (`dispatch message`, `dispatch comment`, `dispatch ask`);
469
+ `dispatch read` writes the pictures what it shows embeds to files and prints their paths. Both are in [Documents](skill://dispatch/references/documents.md), "Artifacts".
482
470
 
483
471
  A BTW, Aside or Steer frame, or a message from the Agents page, is answered as
484
472
  [Targeted and direct messages](skill://dispatch/references/messages.md) says.
@@ -1,7 +1,7 @@
1
1
  # Authentication and the HTTP API
2
2
 
3
- `skill://dispatch` sends you here when you set up a Dispatch token, or need a route the tools do
4
- not cover.
3
+ `skill://dispatch` sends you here when you set up a Dispatch token, or need a route the `dispatch`
4
+ commands do not cover.
5
5
 
6
6
  ## Agent authentication
7
7
 
@@ -19,7 +19,7 @@ is the callback registered on the sign-in pool's app client
19
19
 
20
20
  ### Finding a route
21
21
 
22
- The tools cover the everyday surface. For anything else, ask the server: `GET /api/v1` (no
22
+ The `dispatch` commands cover the everyday surface. For anything else, ask the server: `GET /api/v1` (no
23
23
  credential) returns every route as `{method, path, auth, description}` sorted by path — `auth`
24
24
  is `public`, `any` (a human or a bearer), `human` (a bearer gets `403 HUMAN_ONLY`), or `bearer`.
25
25
  A path Dispatch does not serve under `/api` or `/v1` answers
@@ -33,7 +33,7 @@ every issue your filters match. Walking the pages with the next `offset` is exac
33
33
  listing does not change: an issue that enters or leaves what the filters match, or whose status or
34
34
  rank changes, between two reads shifts rows across a page boundary, and one issue comes back twice
35
35
  and another never, even when you stop at `total`. The unpaged array is the only exact set one read
36
- gives. `cursor` is `400 INVALID_QUERY`. `dispatch_issues` pages for you.
36
+ gives. `cursor` is `400 INVALID_QUERY`. `dispatch issues` pages for you.
37
37
  The event logs page with `after` or `before` and `limit`, and `GET /api/v1/search` takes a `limit`
38
38
  of at most 50 and has no next page. Every other route answers without paging and ignores a paging
39
39
  parameter.
@@ -7,11 +7,8 @@ thread. Whether to ask at all, and how to write the question, is "Before you ask
7
7
 
8
8
  ## Who answers an ask
9
9
 
10
- An ask goes to the issue's assignee: their Inbox opens on **Mine**, which lists asks on the issues they hold plus an Unassigned band; an ask on an unassigned issue waits in that band for someone to take it. Find out who Dispatch takes you for with:
11
- ```ts
12
- dispatch_whoami({})
13
- ```
14
- It returns `details` `{ session, owner, service }`: `owner` is the lowercase email of the person whose personal token you run under, or `null` under the shared token or a verified service token, in which case `service` is that token's subject (`system:serviceaccount:<namespace>:<name>`, `null` otherwise) and every write you make is rendered `(as <namespace>/<name>)` — the namespace is kept because every namespace has a `default` service account, and a subject that is not a Kubernetes one is shown whole. An issue you create without `assignee` goes to your owner; with no owner it inherits its parent's assignee, or stays unassigned without a parent. If an issue you are asking on is unassigned and the answer matters, assign it to your owner (`PATCH /api/v1/issues/{key}` with `{"assignee": "<email>"}`; any authenticated caller may reassign, and an email nobody has signed in with is refused with `ASSIGNEE_NOT_ALLOWED`) or name in the question who should answer it. Never reassign an issue a human holds to get an answer faster: that is the human's call.
10
+ An ask goes to the issue's assignee: their Inbox opens on **Mine**, which lists asks on the issues they hold plus an Unassigned band; an ask on an unassigned issue waits in that band for someone to take it. Find out who Dispatch takes you for with `dispatch whoami`.
11
+ It prints whom the session acts for: the lowercase email of the person whose personal token you run under, or, under the shared token or a verified service token, that it has no owner — naming the service as `<namespace>/<name>` for a service token (`system:serviceaccount:<namespace>:<name>`), which is also how every write you make is rendered (`(as <namespace>/<name>)`) — the namespace is kept because every namespace has a `default` service account, and a subject that is not a Kubernetes one is shown whole. An issue you create without `--assignee` goes to your owner; with no owner it inherits its parent's assignee, or stays unassigned without a parent. If an issue you are asking on is unassigned and the answer matters, assign it to your owner (`PATCH /api/v1/issues/{key}` with `{"assignee": "<email>"}`; any authenticated caller may reassign, and an email nobody has signed in with is refused with `ASSIGNEE_NOT_ALLOWED`) or name in the question who should answer it. Never reassign an issue a human holds to get an answer faster: that is the human's call.
15
12
 
16
13
  ## Handing a human a to-do, editing, resolving, and replying
17
14
 
@@ -20,41 +17,28 @@ A to-do handed to a human still follows the question and options contract that `
20
17
  answer. There is no fixed vocabulary and the server treats no label specially.
21
18
  If an outcome needs a reason, say so in that option's description, and the human's free-text answer
22
19
  carries it:
23
- ```ts
24
- dispatch_ask({
25
- issue: "DSP-42",
26
- question:
27
- "The verified fix remains unavailable in production because deployment is pending. Deploying changes production and could expose a deployment problem. How should we proceed? Recommendation: deploy the verified fix now because it restores the intended behavior.",
28
- options: [
29
- { label: "Deploy the verified fix", description: "Restores the fix but can expose a deployment problem." },
30
- { label: "Hold the deploy", description: "Avoids a production change now but leaves production without the fix." },
31
- ],
32
- })
20
+ ```bash
21
+ dispatch ask --issue DSP-42 \
22
+ --question 'The verified fix remains unavailable in production because deployment is pending. Deploying changes production and could expose a deployment problem. How should we proceed? Recommendation: deploy the verified fix now because it restores the intended behavior.' \
23
+ --option 'Deploy the verified fix: Restores the fix but can expose a deployment problem.' \
24
+ --option 'Hold the deploy: Avoids a production change now but leaves production without the fix.'
33
25
  ```
34
26
 
35
- Correct or refine an open ask in place instead of opening a second question:
36
- ```ts
37
- dispatch_edit_ask({
38
- ask,
39
- question?,
40
- options?: { label, description? }[],
41
- multiple?,
42
- urgency?,
43
- })
44
- ```
45
- At least one field besides `ask` is required. Use this only while the same decision remains open: it keeps the prior text in the event
27
+ Correct or refine an open ask in place instead of opening a second question, with
28
+ `dispatch edit-ask --ask <id>` and any of `--question`, `--option` (repeated), `--multiple` and `--urgency`.
29
+ At least one of them is required. Use this only while the same decision remains open: it keeps the prior text in the event
46
30
  log and invalidates any answer draft against the prior `edited_at` revision, so the human sees the new wording and explicitly reconfirms.
47
31
  An answered or resolved ask cannot be edited. If the decision is moot or superseded, retract the old ask and open a new one.
48
32
  A human can change an answer they gave. The change arrives as another `ask.answered` frame whose
49
33
  `previous_answer` names the answer it replaced, through the same route the first answer took, and
50
- `dispatch_read` of the ask lists every earlier answer. Act on the newest answer.
34
+ `dispatch read --ref dispatch://KEY/ask/<id>` lists every earlier answer. Act on the newest answer.
51
35
 
52
- An ask that lives as an `ask` block in a document keeps its question and options in the block, and `dispatch_edit_ask` writes the
53
- block along with the row, so the edit stands and the document reads the same. It changes only the fields you name: pass `urgency`
54
- alone and the question's own wording, formatting, links and comment anchors are untouched. Pass `question` or `options` and that part
36
+ An ask that lives as an `ask` block in a document keeps its question and options in the block, and `dispatch edit-ask` writes the
37
+ block along with the row, so the edit stands and the document reads the same. It changes only the fields you name: pass `--urgency`
38
+ alone and the question's own wording, formatting, links and comment anchors are untouched. Pass `--question` or `--option` and that part
55
39
  is rewritten, so anchors inside the text you replaced move as they would for any document edit. Either way it is a document edit: it
56
40
  writes a new version, which on a spec awaiting approval closes the design gate until the new version is approved. Editing the block
57
- with `dispatch_doc_edit` works too and is the way to change anything else about it, including adding formatting to a question.
41
+ with `dispatch doc-edit` works too and is the way to change anything else about it, including adding formatting to a question.
58
42
  Re-sending a field unchanged rewrites nothing, so retrying the whole ask is safe.
59
43
  Text the block cannot carry back unchanged is refused outright, naming the field and writing nothing - an option label containing
60
44
  `": "`, the separator between a label and its description, is one example of text that cannot survive the round trip. Blank lines separate paragraphs;
@@ -62,51 +46,47 @@ a single newline is kept as a line break.
62
46
 
63
47
  An ask stays open until a human answers, unless its question no longer needs that answer. Retract a moot or superseded question, or
64
48
  self-resolve one after finding the answer:
65
- ```ts
66
- dispatch_resolve_ask({
67
- ask,
68
- kind: "retracted",
69
- reason: "A newer ask supersedes this question.",
70
- })
49
+ ```bash
50
+ dispatch resolve-ask --ask <id> --kind retracted --reason 'A newer ask supersedes this question.'
71
51
  ```
72
52
  Use `retracted` when the question is obsolete and `resolved` when you found the answer. Include the reason because the question remains
73
53
  in its Conversation card and reply thread; a reason beginning `removed from the document in version` is refused, because that is how a
74
54
  retraction the document's own settlement wrote is recognised. Resolving a block ask records it in the block too, so it stays resolved
75
- however the document moves afterwards. `dispatch_doc_edit` refuses deleting the block of an open ask; a block removed another way, by a
76
- whole-document `dispatch_artifact` replace or by a person in the browser, closes its ask, and putting the block back reopens it. Resolution is not an answer: it never records a human decision, and an answered ask cannot be
77
- resolved. A human may reply to an open or answered ask; so may you, e.g. after finding the answer — use `reply_to_ask` on
78
- `dispatch_comment` (mutually exclusive with `reply_to`).
79
- A review comment you opened has its own closer, `dispatch_resolve_comment` — see
55
+ however the document moves afterwards. `dispatch doc-edit` refuses deleting the block of an open ask; a block removed another way, by a
56
+ whole-document `dispatch artifact` replace or by a person in the browser, closes its ask, and putting the block back reopens it. Resolution is not an answer: it never records a human decision, and an answered ask cannot be
57
+ resolved. A human may reply to an open or answered ask; so may you, e.g. after finding the answer — use `--reply-to-ask` on
58
+ `dispatch comment` (mutually exclusive with `--reply-to`).
59
+ A review comment you opened has its own closer, `dispatch resolve-comment` — see
80
60
  [Comments and suggestions](skill://dispatch/references/documents.md).
81
61
 
82
62
  A human answers or asks back from the same field; a question-shaped free-text answer is offered as a clarification first. A human
83
63
  reply while your ask is still open (the delivered `comment.created` carries `ask_state: open`) is a request for clarification, not
84
64
  an answer: the human did not understand the question or needs more before choosing. The ask now waits on you in their Inbox. Answer
85
- in the same thread with `dispatch_comment({ reply_to_ask })`, or reword the question itself with `dispatch_edit_ask` when the wording
65
+ in the same thread with `dispatch comment --reply-to-ask <id>`, or reword the question itself with `dispatch edit-ask` when the wording
86
66
  was the problem; either puts the ask back in front of them. Do not open a second ask.
87
67
 
88
68
  Every reply to an open ask says whose turn it is next, and the Inbox files the ask by that, not by who spoke last. Your plain reply
89
- (`turn` omitted, or `turn: "human"`) hands the turn to the human: the ask returns to their `Waiting on you`. When you are not done
69
+ (`--turn` omitted, or `--turn human`) hands the turn to the human: the ask returns to their `Waiting on you`. When you are not done
90
70
  yet — "dispatched two auditors, back with results", "checking the release branch, back shortly", any working-on-it note — reply with
91
- `turn: "agent"`: the note lands in the thread, the ask stays under `Waiting on agents`, and the human is not told to act. Use
92
- `turn: "agent"` for every progress note and `turn: "human"` (the default) only when you need them. A human's reply always hands the
93
- turn to you. The result names the state (`ask now waiting on agent` / `human`), the delivered `comment.created` carries it as
71
+ `--turn agent`: the note lands in the thread, the ask stays under `Waiting on agents`, and the human is not told to act. Use
72
+ `--turn agent` for every progress note and `--turn human` (the default) only when you need them. A human's reply always hands the
73
+ turn to you. The output names the state (`ask now waiting on agent` / `human`), the delivered `comment.created` carries it as
94
74
  `ask_waiting_on`, and every ask read carries it as `waiting_on`.
95
75
 
96
76
  ## Following
97
77
 
98
78
  An ask has followers: every session that wrote to it — the session that opened it and every session that replied with
99
- `dispatch_comment({ reply_to_ask })` — plus any session a human adds from the ask card. The ask's answer, edits, resolution, and
79
+ `dispatch comment --reply-to-ask` — plus any session a human adds from the ask card. The ask's answer, edits, resolution, and
100
80
  every reply on it reach each follower's own agent topic directly, whether or not the writer was a human and whatever the issue's
101
- route. The tool result says so (`You follow this ask: its answer and replies reach you directly.`) and carries `details.follows.ask`;
102
- the host tells you once per ask. Leave a thread you no longer need, or rejoin one, with:
81
+ route. The output says so (`You follow this ask: its answer and replies reach you directly.`) once per ask.
82
+ Leave a thread you no longer need, or rejoin one, with:
103
83
 
104
- ```ts
105
- dispatch_follow({ ask, action: "follow" | "unfollow" })
84
+ ```bash
85
+ dispatch follow --ask <id> --action unfollow # or --action follow
106
86
  ```
107
87
 
108
- `ask` is the full ask id or a `dispatch://KEY/ask/<id>` reference; `dispatch_follow`, `dispatch_edit_ask`, and
109
- `dispatch_resolve_ask` also take an 8+ hex prefix that is unique among your own open asks, and refuse anything shorter by naming
88
+ `--ask` is the full ask id or a `dispatch://KEY/ask/<id>` reference; `dispatch follow`, `dispatch edit-ask`, and
89
+ `dispatch resolve-ask` also take an 8+ hex prefix that is unique among your own open asks, and refuse anything shorter by naming
110
90
  those asks. A human may also remove you from the ask card; either way you are
111
91
  told with an `ask.follower_removed` notice, and a human adding you arrives as `ask.follower_added`.
112
92