@sjawhar/pi-legion-envoy 0.41.1 → 0.41.2
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/dist/envoy.js +14 -8
- package/dist/legion.js +14 -8
- package/dist/skills/dispatch/SKILL.md +118 -134
- package/dist/skills/legion-architect/SKILL.md +3 -0
- package/dist/skills/legion-worker/SKILL.md +3 -1
- package/package.json +1 -1
package/dist/envoy.js
CHANGED
|
@@ -29663,14 +29663,16 @@ var IssueEventPayloadSchema = object({
|
|
|
29663
29663
|
route: string2().nullish()
|
|
29664
29664
|
});
|
|
29665
29665
|
var ArtifactCreatedEventPayloadSchema = object({
|
|
29666
|
-
artifact: object({ name: string2().optional() }).optional()
|
|
29666
|
+
artifact: object({ id: string2().optional(), slug: string2().optional(), name: string2().optional() }).optional()
|
|
29667
29667
|
});
|
|
29668
29668
|
var ArtifactVersionEventPayloadSchema = object({
|
|
29669
|
+
artifact_id: string2().optional(),
|
|
29669
29670
|
name: string2().optional(),
|
|
29670
29671
|
version: object({ number: number2().optional(), summary: string2().nullish() }).optional(),
|
|
29671
29672
|
diff: string2().optional()
|
|
29672
29673
|
});
|
|
29673
29674
|
var askEventPayloadFields = {
|
|
29675
|
+
id: string2().optional(),
|
|
29674
29676
|
opened_event_id: number2().int().positive(),
|
|
29675
29677
|
question: string2().optional(),
|
|
29676
29678
|
options: array(object({ label: string2().optional() })).nullish(),
|
|
@@ -29708,13 +29710,13 @@ var CommentEventPayloadSchema = object({
|
|
|
29708
29710
|
ask_question: string2().optional(),
|
|
29709
29711
|
anchor: object({ quote: string2().optional() }).nullish(),
|
|
29710
29712
|
suggestion: object({ replace_with: string2().optional() }).nullish(),
|
|
29711
|
-
author: object({ kind: string2(), id: string2()
|
|
29713
|
+
author: object({ kind: string2(), id: string2() }).optional(),
|
|
29712
29714
|
created_at: string2().optional()
|
|
29713
29715
|
});
|
|
29714
29716
|
var MessageEventPayloadSchema = object({
|
|
29715
29717
|
id: string2().optional(),
|
|
29716
29718
|
body: string2().optional(),
|
|
29717
|
-
author: object({ kind: string2(), id: string2()
|
|
29719
|
+
author: object({ kind: string2(), id: string2() }).optional()
|
|
29718
29720
|
});
|
|
29719
29721
|
var ChildStatusEventPayloadSchema = object({
|
|
29720
29722
|
child_key: string2().optional(),
|
|
@@ -29816,7 +29818,7 @@ var dispatchToolSpecs = [
|
|
|
29816
29818
|
},
|
|
29817
29819
|
{
|
|
29818
29820
|
name: "dispatch_ask",
|
|
29819
|
-
description: "Open a durable, answerable decision on an issue or project document. Do not use it for a status update or discussion; " +
|
|
29821
|
+
description: "Open a durable, answerable decision on an issue or project document. Do not use it for a status update or discussion; " + "use dispatch_message instead. Anchor a document question, thread reply_to/reply_to_ask, or cite a dispatch:// " + `reference \u2014 it must be answerable from its own text and anchor alone, never "see above". Question is at most 800 ` + `characters and has at most 8 options. ${OWNER_REFERENCE}`,
|
|
29820
29822
|
arguments: (z) => ({
|
|
29821
29823
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29822
29824
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
@@ -29897,7 +29899,7 @@ var dispatchToolSpecs = [
|
|
|
29897
29899
|
},
|
|
29898
29900
|
{
|
|
29899
29901
|
name: "dispatch_message",
|
|
29900
|
-
description: "Post a
|
|
29902
|
+
description: "Post a note to the issue's Conversation for humans: a status they should see now, or a reply to a human's message. " + "Not a progress ledger (the issue's progress.md artifact), a decision (dispatch_ask), or document feedback " + `(dispatch_comment). Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
29901
29903
|
arguments: (z) => ({
|
|
29902
29904
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
29903
29905
|
body: z.string({ max: 2000 }).describe("Update text, at most 2,000 characters.")
|
|
@@ -29905,7 +29907,7 @@ var dispatchToolSpecs = [
|
|
|
29905
29907
|
},
|
|
29906
29908
|
{
|
|
29907
29909
|
name: "dispatch_doc_edit",
|
|
29908
|
-
description: "Apply deterministic text edits to an issue or project document. Do not use it for review feedback or for reading; use " +
|
|
29910
|
+
description: "Apply deterministic text edits to an issue or project document. Do not use it for review feedback or for reading; use " + "dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. The spec (or any document) holds requirements, " + `design, and decisions \u2014 record progress in the issue's progress.md artifact instead. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
|
|
29909
29911
|
arguments: (z) => ({
|
|
29910
29912
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29911
29913
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
@@ -31846,8 +31848,12 @@ async function resolveArtifact(client, owner, artifactReference) {
|
|
|
31846
31848
|
} catch (error) {
|
|
31847
31849
|
if (!(error instanceof DispatchServiceError) || error.status !== 404)
|
|
31848
31850
|
throw error;
|
|
31849
|
-
const artifacts = await client.listProjectArtifacts(owner.project);
|
|
31850
|
-
const
|
|
31851
|
+
const artifacts = await client.listProjectArtifacts(owner.project, true);
|
|
31852
|
+
const matches = artifacts.filter((candidate) => candidate.name === artifactReference);
|
|
31853
|
+
if (matches.length > 1) {
|
|
31854
|
+
throw new Error(`artifact name ${artifactReference} is ambiguous in project ${owner.project}; ` + `${matches.length} documents share it \u2014 use its slug instead`);
|
|
31855
|
+
}
|
|
31856
|
+
const artifact = matches[0];
|
|
31851
31857
|
if (!artifact)
|
|
31852
31858
|
throw error;
|
|
31853
31859
|
return { owner, artifact };
|
package/dist/legion.js
CHANGED
|
@@ -29662,14 +29662,16 @@ var IssueEventPayloadSchema = object({
|
|
|
29662
29662
|
route: string2().nullish()
|
|
29663
29663
|
});
|
|
29664
29664
|
var ArtifactCreatedEventPayloadSchema = object({
|
|
29665
|
-
artifact: object({ name: string2().optional() }).optional()
|
|
29665
|
+
artifact: object({ id: string2().optional(), slug: string2().optional(), name: string2().optional() }).optional()
|
|
29666
29666
|
});
|
|
29667
29667
|
var ArtifactVersionEventPayloadSchema = object({
|
|
29668
|
+
artifact_id: string2().optional(),
|
|
29668
29669
|
name: string2().optional(),
|
|
29669
29670
|
version: object({ number: number2().optional(), summary: string2().nullish() }).optional(),
|
|
29670
29671
|
diff: string2().optional()
|
|
29671
29672
|
});
|
|
29672
29673
|
var askEventPayloadFields = {
|
|
29674
|
+
id: string2().optional(),
|
|
29673
29675
|
opened_event_id: number2().int().positive(),
|
|
29674
29676
|
question: string2().optional(),
|
|
29675
29677
|
options: array(object({ label: string2().optional() })).nullish(),
|
|
@@ -29707,13 +29709,13 @@ var CommentEventPayloadSchema = object({
|
|
|
29707
29709
|
ask_question: string2().optional(),
|
|
29708
29710
|
anchor: object({ quote: string2().optional() }).nullish(),
|
|
29709
29711
|
suggestion: object({ replace_with: string2().optional() }).nullish(),
|
|
29710
|
-
author: object({ kind: string2(), id: string2()
|
|
29712
|
+
author: object({ kind: string2(), id: string2() }).optional(),
|
|
29711
29713
|
created_at: string2().optional()
|
|
29712
29714
|
});
|
|
29713
29715
|
var MessageEventPayloadSchema = object({
|
|
29714
29716
|
id: string2().optional(),
|
|
29715
29717
|
body: string2().optional(),
|
|
29716
|
-
author: object({ kind: string2(), id: string2()
|
|
29718
|
+
author: object({ kind: string2(), id: string2() }).optional()
|
|
29717
29719
|
});
|
|
29718
29720
|
var ChildStatusEventPayloadSchema = object({
|
|
29719
29721
|
child_key: string2().optional(),
|
|
@@ -29815,7 +29817,7 @@ var dispatchToolSpecs = [
|
|
|
29815
29817
|
},
|
|
29816
29818
|
{
|
|
29817
29819
|
name: "dispatch_ask",
|
|
29818
|
-
description: "Open a durable, answerable decision on an issue or project document. Do not use it for a status update or discussion; " +
|
|
29820
|
+
description: "Open a durable, answerable decision on an issue or project document. Do not use it for a status update or discussion; " + "use dispatch_message instead. Anchor a document question, thread reply_to/reply_to_ask, or cite a dispatch:// " + `reference \u2014 it must be answerable from its own text and anchor alone, never "see above". Question is at most 800 ` + `characters and has at most 8 options. ${OWNER_REFERENCE}`,
|
|
29819
29821
|
arguments: (z) => ({
|
|
29820
29822
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29821
29823
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
@@ -29896,7 +29898,7 @@ var dispatchToolSpecs = [
|
|
|
29896
29898
|
},
|
|
29897
29899
|
{
|
|
29898
29900
|
name: "dispatch_message",
|
|
29899
|
-
description: "Post a
|
|
29901
|
+
description: "Post a note to the issue's Conversation for humans: a status they should see now, or a reply to a human's message. " + "Not a progress ledger (the issue's progress.md artifact), a decision (dispatch_ask), or document feedback " + `(dispatch_comment). Body is at most 2,000 characters. ${ISSUE_REFERENCE}`,
|
|
29900
29902
|
arguments: (z) => ({
|
|
29901
29903
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
29902
29904
|
body: z.string({ max: 2000 }).describe("Update text, at most 2,000 characters.")
|
|
@@ -29904,7 +29906,7 @@ var dispatchToolSpecs = [
|
|
|
29904
29906
|
},
|
|
29905
29907
|
{
|
|
29906
29908
|
name: "dispatch_doc_edit",
|
|
29907
|
-
description: "Apply deterministic text edits to an issue or project document. Do not use it for review feedback or for reading; use " +
|
|
29909
|
+
description: "Apply deterministic text edits to an issue or project document. Do not use it for review feedback or for reading; use " + "dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. The spec (or any document) holds requirements, " + `design, and decisions \u2014 record progress in the issue's progress.md artifact instead. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
|
|
29908
29910
|
arguments: (z) => ({
|
|
29909
29911
|
issue: z.string().describe(ISSUE_REFERENCE).optional(),
|
|
29910
29912
|
project: z.string().describe("Project key owning the document.").optional(),
|
|
@@ -32286,8 +32288,12 @@ async function resolveArtifact(client, owner, artifactReference) {
|
|
|
32286
32288
|
} catch (error) {
|
|
32287
32289
|
if (!(error instanceof DispatchServiceError) || error.status !== 404)
|
|
32288
32290
|
throw error;
|
|
32289
|
-
const artifacts = await client.listProjectArtifacts(owner.project);
|
|
32290
|
-
const
|
|
32291
|
+
const artifacts = await client.listProjectArtifacts(owner.project, true);
|
|
32292
|
+
const matches = artifacts.filter((candidate) => candidate.name === artifactReference);
|
|
32293
|
+
if (matches.length > 1) {
|
|
32294
|
+
throw new Error(`artifact name ${artifactReference} is ambiguous in project ${owner.project}; ` + `${matches.length} documents share it \u2014 use its slug instead`);
|
|
32295
|
+
}
|
|
32296
|
+
const artifact = matches[0];
|
|
32291
32297
|
if (!artifact)
|
|
32292
32298
|
throw error;
|
|
32293
32299
|
return { owner, artifact };
|
|
@@ -5,17 +5,17 @@ description: "Use when asking Sami a question, updating the spec, commenting on
|
|
|
5
5
|
|
|
6
6
|
# Dispatch
|
|
7
7
|
|
|
8
|
-
Dispatch is your issue's or project document's living spec, asks, comments, and artifacts. The
|
|
9
|
-
|
|
8
|
+
Dispatch is your issue's or project document's living spec, asks, comments, and artifacts. The transcript is your scratch pad. Anything
|
|
9
|
+
meant for a human goes through a `dispatch_*` tool.
|
|
10
10
|
|
|
11
|
-
The server enforces high signal: an ask question is at most 800 characters with at most eight
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
The server enforces high signal: an ask question is at most 800 characters with at most eight options; comment and message bodies are at
|
|
12
|
+
most 2,000 characters; an artifact is at most 25 MiB. It refuses over-limit input; it never truncates it. GitHub threads and markers no
|
|
13
|
+
longer exist.
|
|
14
14
|
|
|
15
15
|
## Writing a spec
|
|
16
16
|
|
|
17
|
-
A spec is a decision record for the human who decides and the implementer who builds, not a
|
|
18
|
-
|
|
17
|
+
A spec is a decision record for the human who decides and the implementer who builds, not a transcript of your thinking. Use exactly
|
|
18
|
+
these document headings in this order.
|
|
19
19
|
|
|
20
20
|
| Section | Required content | Form |
|
|
21
21
|
| --- | --- | --- |
|
|
@@ -35,32 +35,23 @@ transcript of your thinking. Use exactly these document headings in this order.
|
|
|
35
35
|
- Do not hedge with “might” or “could consider.”
|
|
36
36
|
- Do not use TBD, TODO, or placeholders; an open item is a Decision needed.
|
|
37
37
|
- Keep each section to one screen; work that exceeds one screen per section is two specs.
|
|
38
|
-
- Update the spec in place as decisions land
|
|
39
|
-
|
|
40
|
-
### Self-review
|
|
41
|
-
|
|
42
|
-
- [ ] No placeholders remain.
|
|
43
|
-
- [ ] No sections conflict.
|
|
44
|
-
- [ ] The spec covers one implementation plan's worth of work.
|
|
45
|
-
- [ ] Every requirement has exactly one reading.
|
|
38
|
+
- Update the spec in place as decisions land: the spec is the record, comments are the discussion.
|
|
39
|
+
- Before sending it: no sections conflict, and every requirement has exactly one reading.
|
|
46
40
|
|
|
47
41
|
## Your owner
|
|
48
42
|
|
|
49
|
-
Every session works on an issue or project document. Legion pre-fills `issue` from
|
|
50
|
-
`
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
`design-notes` identifies `dispatch://CORE/artifact/design-notes`. On first use, an external
|
|
55
|
-
issue reference creates its native issue in the project configured for that repository in
|
|
56
|
-
Dispatch Settings, then falls back to `DISPATCH_DEFAULT_PROJECT`.
|
|
43
|
+
Every session works on an issue or project document. Legion pre-fills `issue` from `LEGION_ISSUE`: use a native issue key such as
|
|
44
|
+
`LEGION-3`, an external `owner/repo#n` reference, or a bare positive number (resolved against the cwd repository). Otherwise pass
|
|
45
|
+
exactly one owner to every owner-scoped tool: `issue` for an issue, or `project` and `artifact` for an unlinked project document (see
|
|
46
|
+
[References](#references) for the resulting ref shape). On first use, an external issue reference creates its native issue in the
|
|
47
|
+
project configured for that repository in Dispatch Settings, then falls back to `DISPATCH_DEFAULT_PROJECT`.
|
|
57
48
|
|
|
58
49
|
Architects create newly tracked child work with:
|
|
59
50
|
```ts
|
|
60
51
|
dispatch_issue({ project, title, parent?, external?, spec?, force? })
|
|
61
52
|
```
|
|
62
|
-
It returns `details` `{ issue, topic }`. Use `dispatch_issue` only to create an issue; never use
|
|
63
|
-
|
|
53
|
+
It returns `details` `{ issue, topic }`. Use `dispatch_issue` only to create an issue; never use it to park a question. When `spec` is
|
|
54
|
+
supplied, follow [Writing a spec](#writing-a-spec).
|
|
64
55
|
|
|
65
56
|
## Search first
|
|
66
57
|
|
|
@@ -68,13 +59,12 @@ Before you create an issue or start a design document, search:
|
|
|
68
59
|
```ts
|
|
69
60
|
dispatch_search({ query, project?, limit? })
|
|
70
61
|
```
|
|
71
|
-
It returns every issue, document, comment, ask, and message that contains the words, with the
|
|
72
|
-
|
|
73
|
-
|
|
62
|
+
It returns every issue, document, comment, ask, and message that contains the words, with the issue key and a link. Cite the hit you
|
|
63
|
+
build on (`dispatch://KEY` or the document reference), or state "no prior issue" in the spec. Websearch syntax applies: `"merge queue"`,
|
|
64
|
+
`-daemon`, `OR`.
|
|
74
65
|
|
|
75
|
-
`dispatch_issue` refuses a title that near-duplicates an issue in the same project and returns
|
|
76
|
-
|
|
77
|
-
call with `force: true` when it is genuinely new work.
|
|
66
|
+
`dispatch_issue` refuses a title that near-duplicates an issue in the same project and returns the candidates (`POSSIBLE_DUPLICATE`).
|
|
67
|
+
Read them; reference the existing issue, or repeat the call with `force: true` when it is genuinely new work.
|
|
78
68
|
|
|
79
69
|
## Asking
|
|
80
70
|
|
|
@@ -91,14 +81,14 @@ dispatch_ask({
|
|
|
91
81
|
anchor?: { artifact, quote, occurrence? },
|
|
92
82
|
})
|
|
93
83
|
```
|
|
94
|
-
It returns `details` `{ issue, topic, ask }` for an issue or `{ project, artifact, document,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
84
|
+
It returns `details` `{ issue, topic, ask }` for an issue or `{ project, artifact, document, topic, ask }` for a project document.
|
|
85
|
+
Options are buttons: never enumerate choices in prose. Put the recommendation in `question`, and put each selectable choice in
|
|
86
|
+
`options`. Anchor a document question with `anchor: { artifact, quote, occurrence? }`; `occurrence` is zero-based and selects a repeated
|
|
87
|
+
quote, and an anchor whose quote later disappears becomes orphaned but stays readable against its original document version.
|
|
88
|
+
|
|
89
|
+
An ask must be answerable from its own text and its anchor alone. Anchor a question about a document passage with `anchor`; thread one
|
|
90
|
+
about a comment with `reply_to`; thread a follow-up on your own ask with `reply_to_ask`; cite anything else with a `dispatch://`
|
|
91
|
+
reference (see [References](#references)). Never write "see above", "the message above", or "as attached".
|
|
102
92
|
|
|
103
93
|
Correct or refine an open ask in place instead of opening a second question:
|
|
104
94
|
```ts
|
|
@@ -110,12 +100,11 @@ dispatch_edit_ask({
|
|
|
110
100
|
urgency?,
|
|
111
101
|
})
|
|
112
102
|
```
|
|
113
|
-
At least one field besides `ask` is required. Use this only while the same decision remains
|
|
114
|
-
|
|
115
|
-
If the decision is moot or superseded, retract the old ask and open a new one.
|
|
103
|
+
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
|
|
104
|
+
log. An answered or resolved ask cannot be edited. If the decision is moot or superseded, retract the old ask and open a new one.
|
|
116
105
|
|
|
117
|
-
An ask stays open until a human answers, unless its question no longer needs that answer. Retract a
|
|
118
|
-
|
|
106
|
+
An ask stays open until a human answers, unless its question no longer needs that answer. Retract a moot or superseded question, or
|
|
107
|
+
self-resolve one after finding the answer:
|
|
119
108
|
```ts
|
|
120
109
|
dispatch_resolve_ask({
|
|
121
110
|
ask,
|
|
@@ -123,32 +112,31 @@ dispatch_resolve_ask({
|
|
|
123
112
|
reason: "A newer ask supersedes this question.",
|
|
124
113
|
})
|
|
125
114
|
```
|
|
126
|
-
Use `retracted` when the question is obsolete and `resolved` when you found the answer. Include the
|
|
127
|
-
|
|
128
|
-
|
|
115
|
+
Use `retracted` when the question is obsolete and `resolved` when you found the answer. Include the reason because the question remains
|
|
116
|
+
in its Conversation card and reply thread. Resolution is not an answer: it never records a human decision, and an answered ask cannot be
|
|
117
|
+
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
|
|
118
|
+
`dispatch_comment` (mutually exclusive with `reply_to`).
|
|
129
119
|
|
|
130
|
-
|
|
131
|
-
(the asker) can reply too — e.g. acknowledging a clarifying question, or following up after the
|
|
132
|
-
answer. Use `reply_to_ask` on `dispatch_comment` to reply under your own ask; it is mutually
|
|
133
|
-
exclusive with `reply_to`.
|
|
120
|
+
## The Spec
|
|
134
121
|
|
|
135
|
-
|
|
136
|
-
|
|
122
|
+
The spec holds requirements, design, acceptance, decisions, and rejected alternatives, structured per [Writing a spec](#writing-a-spec).
|
|
123
|
+
It changes only when a decision or requirement changes, and every version that records one is named with `summary`. Never write
|
|
124
|
+
progress, status, timestamps, an "Update HH:MMZ" section, a PR list, or handoff notes into the spec — that belongs in
|
|
125
|
+
[Progress](#progress) instead.
|
|
137
126
|
|
|
138
127
|
Read the current document before changing it:
|
|
139
128
|
|
|
140
129
|
```ts
|
|
141
130
|
dispatch_doc_read({ issue?, project?, artifact?, version?, ref? })
|
|
142
131
|
```
|
|
143
|
-
It returns live or versioned markdown with open marks. `issue` with an omitted `artifact`
|
|
144
|
-
|
|
145
|
-
`dispatch://PROJECT/artifact/<slug>` ref supplies both. Then write narrative with:
|
|
132
|
+
It returns live or versioned markdown with open marks. `issue` with an omitted `artifact` reads the issue specification; a project needs
|
|
133
|
+
`artifact`; and a `dispatch://PROJECT/artifact/<slug>` ref supplies both. Then write with:
|
|
146
134
|
|
|
147
135
|
```ts
|
|
148
136
|
dispatch_doc_edit({ issue?, project?, artifact, ops, summary? })
|
|
149
137
|
```
|
|
150
|
-
It returns issue or project-document owner details plus `applied`, optional `version`, and its
|
|
151
|
-
|
|
138
|
+
It returns issue or project-document owner details plus `applied`, optional `version`, and its write `topic`. `ops` is an array of this
|
|
139
|
+
exact `EditOp` shape:
|
|
152
140
|
|
|
153
141
|
```ts
|
|
154
142
|
type EditOp = {
|
|
@@ -162,23 +150,40 @@ type EditOp = {
|
|
|
162
150
|
};
|
|
163
151
|
```
|
|
164
152
|
|
|
165
|
-
Target `replace` and `delete` by the document's plain text: inline-code and link text match
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
span separate blocks into separate operations.
|
|
153
|
+
Target `replace` and `delete` by the document's plain text: inline-code and link text match without Markdown syntax, and a table-cell
|
|
154
|
+
anchor is its cell text. Quote code-block contents without their Markdown fences. A quote must stay within one textblock; split changes
|
|
155
|
+
that span separate blocks into separate operations.
|
|
169
156
|
|
|
170
|
-
`replace` requires `find` and `with`; `delete` requires `find`; `insert` requires `markdown`
|
|
171
|
-
|
|
172
|
-
`"
|
|
173
|
-
|
|
157
|
+
`replace` requires `find` and `with`; `delete` requires `find`; `insert` requires `markdown` and exactly one of `after` or `before`. An
|
|
158
|
+
insert anchor is a quote, `"start"`, `"end"`, or `"heading:Title"`. Ordinary inserts create a sibling block before or after the quote or
|
|
159
|
+
heading's enclosing document block; `"start"` and `"end"` select the document edges. At a table-cell quote, a body-row fragment (no
|
|
160
|
+
header or delimiter rows) extends that table before or after the matched row instead; short rows are padded, wider rows are rejected,
|
|
161
|
+
and deleting a cell's quoted text removes only that text.
|
|
174
162
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
163
|
+
Use `replace` for inline continuation. Use zero-based `occurrence` for a repeated target; re-read a missing or ambiguous target before
|
|
164
|
+
retrying. Pass `summary` to name the version when recording a decision.
|
|
165
|
+
|
|
166
|
+
## Progress
|
|
167
|
+
|
|
168
|
+
Every issue you work has one progress artifact, `progress.md` — for humans reading later and for your own successor after compaction. It
|
|
169
|
+
is never a wake signal.
|
|
170
|
+
|
|
171
|
+
Create it once, on first use:
|
|
172
|
+
```ts
|
|
173
|
+
dispatch_artifact({ issue, name: "progress.md", content: "### 2026-09-11 15:00Z - Started\n..." })
|
|
174
|
+
```
|
|
175
|
+
The server slugs `progress.md` to `progress-md`; address every later edit with that slug. Append — never edit or remove an earlier
|
|
176
|
+
entry:
|
|
177
|
+
```ts
|
|
178
|
+
dispatch_doc_edit({
|
|
179
|
+
issue,
|
|
180
|
+
artifact: "progress-md",
|
|
181
|
+
ops: [{ op: "insert", after: "end", markdown: "### 2026-09-11 16:10Z - Blocked\n..." }],
|
|
182
|
+
})
|
|
183
|
+
```
|
|
184
|
+
Newest entry last. Each entry is `### <UTC time> - <headline>` followed by 1-5 lines: what changed (cite `dispatch://` refs or PR
|
|
185
|
+
links), what is blocked and on whom, and what is next. `.legion/<phase>.json` is the durable machine handoff between phases;
|
|
186
|
+
`progress.md` is the human-readable narrative for the same work — keep both, never conflate one for the other.
|
|
182
187
|
|
|
183
188
|
## Comments and suggestions
|
|
184
189
|
|
|
@@ -188,14 +193,11 @@ Add feedback with:
|
|
|
188
193
|
dispatch_comment({ issue?, project?, artifact?, quote?, occurrence?, body, reply_to?, reply_to_ask? })
|
|
189
194
|
```
|
|
190
195
|
|
|
191
|
-
It returns issue or project-document owner details plus `comment` and, for writes, `topic`.
|
|
192
|
-
`
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
`
|
|
196
|
-
mutually exclusive. Comments are edited only by their author from the dashboard. A delivered
|
|
197
|
-
`comment.created` event carries the comment `id`; reply to it with
|
|
198
|
-
`dispatch_comment({ reply_to: <id> })`.
|
|
196
|
+
It returns issue or project-document owner details plus `comment` and, for writes, `topic`. `quote` requires `artifact`; omit both for a
|
|
197
|
+
floating issue comment. A reply (`reply_to`/`reply_to_ask`) takes no `quote`; it belongs to its parent's anchor. Use `reply_to` to
|
|
198
|
+
continue a comment thread at its root; a reply to a resolved thread reopens it. Use `reply_to_ask` to reply directly under a question
|
|
199
|
+
asked with `dispatch_ask`. Comments are edited only by their author from the dashboard. A delivered `comment.created` event carries the
|
|
200
|
+
comment `id`; reply to it with `dispatch_comment({ reply_to: <id> })`.
|
|
199
201
|
|
|
200
202
|
Propose an exact replacement instead of describing it:
|
|
201
203
|
|
|
@@ -203,15 +205,9 @@ Propose an exact replacement instead of describing it:
|
|
|
203
205
|
dispatch_suggest({ issue?, project?, artifact, quote, replace_with, body?, occurrence? })
|
|
204
206
|
```
|
|
205
207
|
|
|
206
|
-
It returns issue or project-document owner details plus `comment` and its write `topic`. A
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
before retrying. `INVALID_ANCHOR` requires exactly one nonempty anchor `quote` or `mark_id`;
|
|
210
|
-
`ANCHOR_MISSING` means a browser mark was not observed in the live tree, and
|
|
211
|
-
`ANCHOR_ORPHANED` means its marked text no longer exists. `INVALID_MARKDOWN` and `DOC_SCHEMA`
|
|
212
|
-
reject Markdown or a live tree outside the Proof schema. `INVALID_OP` names a malformed edit;
|
|
213
|
-
`CAP_EXCEEDED` never truncates; `ISSUE_CLOSED` rejects a write. `ACTOR_KIND` and `ROUTE_INVALID`
|
|
214
|
-
reject an invalid actor or route.
|
|
208
|
+
It returns issue or project-document owner details plus `comment` and its write `topic`. A human accepts or rejects a suggestion.
|
|
209
|
+
Errors: `TARGET_AMBIGUOUS` (add `occurrence`), `TARGET_NOT_FOUND` (re-read first), `INVALID_ANCHOR`/`ANCHOR_MISSING`/`ANCHOR_ORPHANED`
|
|
210
|
+
(bad, unwritten, or stale quote), `INVALID_MARKDOWN`/`DOC_SCHEMA` (malformed content), `CAP_EXCEEDED`, `ISSUE_CLOSED`.
|
|
215
211
|
|
|
216
212
|
## Artifacts
|
|
217
213
|
|
|
@@ -227,40 +223,37 @@ Or, when the text is already in the call, post a Markdown document directly:
|
|
|
227
223
|
dispatch_artifact({ issue?, project?, name: "spec.md", content: "# Design\n..." })
|
|
228
224
|
```
|
|
229
225
|
|
|
230
|
-
Exactly one of `issue` and `project` is required. A project upload creates an unlinked project
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
same `name` creates its next version. Use `content` when the text is already in the call.
|
|
235
|
-
Address an existing artifact by the slug shown in the upload result or by its filename.
|
|
226
|
+
Exactly one of `issue` and `project` is required. A project upload creates an unlinked project document; it must not include `artifact`.
|
|
227
|
+
Exactly one of `path` and `content` is required. It returns issue or project-document owner details plus `artifact`, `version`, and its
|
|
228
|
+
write `topic`. Uploading the same `name` creates its next version. Address an existing artifact by the slug shown in the upload result
|
|
229
|
+
or by its filename; the slug also arrives on `artifact.created` events.
|
|
236
230
|
|
|
237
231
|
## Messages
|
|
238
232
|
|
|
239
|
-
|
|
233
|
+
Post a note to the issue's Conversation for humans: a short status they should see now (a deploy landed, a blocker appeared), or a reply
|
|
234
|
+
to a human's message.
|
|
240
235
|
|
|
241
236
|
```ts
|
|
242
237
|
dispatch_message({ issue, body })
|
|
243
238
|
```
|
|
244
239
|
|
|
245
|
-
It returns `details` `{ issue, topic, message }`. `body` is capped at 2,000 characters.
|
|
246
|
-
|
|
240
|
+
It returns `details` `{ issue, topic, message }`. `body` is capped at 2,000 characters. It is not a progress ledger (`progress.md`), a
|
|
241
|
+
decision (`dispatch_ask`), or document feedback (`dispatch_comment`). Your message does not wake anyone unless the issue is routed.
|
|
247
242
|
|
|
248
243
|
## What comes back
|
|
249
244
|
|
|
250
|
-
A write result's `details.topic` subscribes the host to its owner. Issue writes use
|
|
251
|
-
`notifications.dispatch.
|
|
252
|
-
|
|
253
|
-
event; `notify` only controls agent wake and routed delivery. After a restart, catch up with:
|
|
245
|
+
A write result's `details.topic` subscribes the host to its owner. Issue writes use `notifications.dispatch.issue.<KEY>.>`;
|
|
246
|
+
project-document writes use `notifications.dispatch.document.<PROJECT>.<SLUG>.>`. The owner topic carries every Dispatch event; `notify`
|
|
247
|
+
only controls agent wake and routed delivery. After a restart, catch up with:
|
|
254
248
|
|
|
255
249
|
```ts
|
|
256
250
|
dispatch_read({ issue?, project?, artifact?, ref? })
|
|
257
251
|
```
|
|
258
252
|
|
|
259
|
-
With an issue ref, it returns the issue summary, open asks, references, and recent events with
|
|
260
|
-
`details` `{
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
reply chain. Reads do not subscribe; use `dispatch_doc_read` for document contents.
|
|
253
|
+
With an issue ref, it returns the issue summary, open asks, references, and recent events with `details` `{ issue }`. With a project
|
|
254
|
+
document owner or ref, it returns a document summary with `details` `{ project, document }`. With an ask ref, it returns that ask's
|
|
255
|
+
question, options, state, answer, and its reply thread. With a comment ref, it returns that comment and its quoted reply chain. Reads do
|
|
256
|
+
not subscribe; use `dispatch_doc_read` for document contents.
|
|
264
257
|
|
|
265
258
|
## References
|
|
266
259
|
|
|
@@ -277,6 +270,8 @@ dispatch://PROJECT/artifact/<slug>/ask/<id>
|
|
|
277
270
|
dispatch://PROJECT/artifact/<slug>/comment/<id>
|
|
278
271
|
```
|
|
279
272
|
|
|
273
|
+
A bare UUID or `KEY#seq` is not a reference; the `dispatch://` form is what Dispatch links and records.
|
|
274
|
+
|
|
280
275
|
## Before / after
|
|
281
276
|
|
|
282
277
|
Before — a wall of text hides the decision and makes the choices unclickable:
|
|
@@ -294,48 +289,37 @@ After — anchor the decision and make each option a button:
|
|
|
294
289
|
dispatch_ask({
|
|
295
290
|
issue: "LEGION-815",
|
|
296
291
|
question:
|
|
297
|
-
"Choose the release gate. Recommendation: ship after release-note review
|
|
292
|
+
"Choose the release gate. Recommendation: ship after release-note review, since the tested deployment is otherwise ready.",
|
|
298
293
|
options: [
|
|
299
|
-
{
|
|
300
|
-
|
|
301
|
-
description: "Keeps the tested release intact and publishes reviewed instructions.",
|
|
302
|
-
},
|
|
303
|
-
{
|
|
304
|
-
label: "Ship now",
|
|
305
|
-
description: "Meets the demo deadline; release notes follow separately.",
|
|
306
|
-
},
|
|
307
|
-
{
|
|
308
|
-
label: "Remove dashboard changes",
|
|
309
|
-
description: "Narrows the release but requires another deployment test.",
|
|
310
|
-
},
|
|
294
|
+
{ label: "Review notes, then ship", description: "Keeps the release intact and reviewed." },
|
|
295
|
+
{ label: "Ship now", description: "Meets the demo deadline; release notes follow later." },
|
|
311
296
|
],
|
|
312
297
|
urgency: "high",
|
|
313
|
-
anchor: {
|
|
314
|
-
artifact: "spec",
|
|
315
|
-
quote: "Release requires reviewed operator instructions before deployment.",
|
|
316
|
-
},
|
|
298
|
+
anchor: { artifact: "spec", quote: "Release requires reviewed operator instructions before deployment." },
|
|
317
299
|
})
|
|
318
300
|
```
|
|
319
301
|
|
|
320
|
-
Before — a
|
|
302
|
+
Before — progress typed once into a message, gone once compaction drops it from context:
|
|
321
303
|
|
|
322
|
-
```
|
|
323
|
-
|
|
304
|
+
```ts
|
|
305
|
+
dispatch_message({ issue: "LEGION-815", body: "Merged the release PR, moving to docs next." })
|
|
324
306
|
```
|
|
325
307
|
|
|
326
|
-
After —
|
|
308
|
+
After — append it to the progress artifact, where the record survives:
|
|
327
309
|
|
|
328
310
|
```ts
|
|
329
311
|
dispatch_doc_edit({
|
|
330
312
|
issue: "LEGION-815",
|
|
331
|
-
artifact: "
|
|
313
|
+
artifact: "progress-md",
|
|
332
314
|
ops: [
|
|
333
315
|
{
|
|
334
|
-
op: "
|
|
335
|
-
|
|
336
|
-
|
|
316
|
+
op: "insert",
|
|
317
|
+
after: "end",
|
|
318
|
+
markdown:
|
|
319
|
+
"### 2026-09-11 15:40Z - Release PR merged\n" +
|
|
320
|
+
"- dispatch://LEGION-815/artifact/spec stays unchanged; this is progress, not a decision.\n" +
|
|
321
|
+
"- Next: docs review.",
|
|
337
322
|
},
|
|
338
323
|
],
|
|
339
|
-
summary: "Recorded merged release",
|
|
340
324
|
})
|
|
341
325
|
```
|
|
@@ -71,6 +71,9 @@ exercise a criterion end to end, building that path is a child issue of this tre
|
|
|
71
71
|
inert until released.
|
|
72
72
|
|
|
73
73
|
Specifications written into Dispatch follow [`skills/dispatch`'s Writing a spec](../dispatch/SKILL.md#writing-a-spec).
|
|
74
|
+
Record ongoing status — wave releases, child closures, blockers — in the issue's `progress.md`
|
|
75
|
+
artifact (see [`skills/dispatch`'s Progress](../dispatch/SKILL.md#progress)), never in the spec
|
|
76
|
+
or a `dispatch_message`.
|
|
74
77
|
|
|
75
78
|
Write one root specification containing the accepted scope, adoption/decomposition,
|
|
76
79
|
waves, acceptance criteria, and integration test. When the config-armed root design gate
|
|
@@ -97,7 +97,9 @@ committed predecessor handoffs in lifecycle order from `$LEGION_WORKSPACE/.legio
|
|
|
97
97
|
Read only files that precede the assigned phase. There is no handoff schema (rejected
|
|
98
98
|
design — no schema validation runs anywhere in this pipeline): write the phase-specific
|
|
99
99
|
fields the next phase and the architect need, consistent with what predecessor phases
|
|
100
|
-
already wrote. The durable copy lives in `$LEGION_WORKSPACE/.legion/<phase>.json
|
|
100
|
+
already wrote. The durable copy lives in `$LEGION_WORKSPACE/.legion/<phase>.json` — the machine
|
|
101
|
+
handoff between phases, not a human-readable status; post that to the issue's `progress.md`
|
|
102
|
+
artifact instead (see [`skills/dispatch`'s Progress](../dispatch/SKILL.md#progress)). If a
|
|
101
103
|
committed handoff conflicts with memory or a prior transcript, the committed file wins: it
|
|
102
104
|
is the copy that survived.
|
|
103
105
|
|