@sjawhar/opencode-legion-envoy 0.31.0 → 0.33.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 +2 -2
- package/dist/src/server.js +12 -8
- package/package.json +1 -1
- package/skills/dispatch/SKILL.md +51 -11
- package/skills/legion-architect/SKILL.md +3 -1
- package/skills/legion-worker/SKILL.md +2 -0
package/README.md
CHANGED
|
@@ -32,8 +32,8 @@ the session working directory, stamps it with the OpenCode session id and title,
|
|
|
32
32
|
`details.topic` as tool metadata so a successful mutation subscribes to that exact Dispatch topic.
|
|
33
33
|
|
|
34
34
|
`dispatch_artifact` accepts exactly one upload source: a local `path`, or inline `content`.
|
|
35
|
-
An architect can post a
|
|
36
|
-
`{ issue, name: "spec.md", content: "# Design"
|
|
35
|
+
An architect can post a specification directly with
|
|
36
|
+
`{ issue, name: "spec.md", content: "# Design" }`.
|
|
37
37
|
|
|
38
38
|
It also maintains the live session registry metadata needed for Envoy to discover OpenCode sessions and their API ports.
|
|
39
39
|
|
package/dist/src/server.js
CHANGED
|
@@ -13603,6 +13603,16 @@ function dispatchToolSchema(spec, z, opts) {
|
|
|
13603
13603
|
return spec.validation === undefined ? z.object(shape, opts) : z.refineObject(shape, spec.validation.check, spec.validation.message, opts);
|
|
13604
13604
|
}
|
|
13605
13605
|
var ISSUE_REFERENCE = "An issue is a native KEY or external owner/repo#n reference; an external reference creates its native issue in the repository's dashboard-configured project or, failing that, the default project (DISPATCH_DEFAULT_PROJECT).";
|
|
13606
|
+
var SPEC_SECTIONS = [
|
|
13607
|
+
"Decisions needed",
|
|
13608
|
+
"Acceptance",
|
|
13609
|
+
"Requirements",
|
|
13610
|
+
"Design",
|
|
13611
|
+
"Errors",
|
|
13612
|
+
"Testing",
|
|
13613
|
+
"Rejected"
|
|
13614
|
+
];
|
|
13615
|
+
var SPEC_WRITING_GUIDANCE = `When writing a spec, use these sections in order: ${SPEC_SECTIONS.join(", ")}. ` + "Every line is a fact, decision, or risk; use tables over prose; see skills/dispatch Writing a spec.";
|
|
13606
13616
|
var ASK_URGENCIES = ["low", "med", "high", "blocking"];
|
|
13607
13617
|
var DOC_EDIT_OPS = ["replace", "delete", "insert"];
|
|
13608
13618
|
var dispatchToolSpecs = [
|
|
@@ -13614,7 +13624,7 @@ var dispatchToolSpecs = [
|
|
|
13614
13624
|
title: z.string().describe("Concise issue title."),
|
|
13615
13625
|
parent: z.string().describe("Optional parent issue.").optional(),
|
|
13616
13626
|
external: z.string().describe("Optional external issue reference.").optional(),
|
|
13617
|
-
spec: z.string().describe(
|
|
13627
|
+
spec: z.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_GUIDANCE}`).optional()
|
|
13618
13628
|
})
|
|
13619
13629
|
},
|
|
13620
13630
|
{
|
|
@@ -13680,7 +13690,7 @@ var dispatchToolSpecs = [
|
|
|
13680
13690
|
},
|
|
13681
13691
|
{
|
|
13682
13692
|
name: "dispatch_doc_edit",
|
|
13683
|
-
description: "Apply deterministic text edits to a document. Do not use it for review feedback or for reading; use " + `dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. ${ISSUE_REFERENCE}`,
|
|
13693
|
+
description: "Apply deterministic text edits to a document. Do not use it for review feedback or for reading; use " + `dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. ${ISSUE_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
|
|
13684
13694
|
arguments: (z) => ({
|
|
13685
13695
|
issue: z.string().describe(ISSUE_REFERENCE),
|
|
13686
13696
|
artifact: z.string().describe("Artifact slug or id for the document."),
|
|
@@ -13714,7 +13724,6 @@ var dispatchToolSpecs = [
|
|
|
13714
13724
|
name: z.string().describe("Artifact filename shown in Dispatch."),
|
|
13715
13725
|
path: z.string().describe("Local path to the file to upload.").optional(),
|
|
13716
13726
|
content: z.string().describe("Inline text to store as a Markdown document.").optional(),
|
|
13717
|
-
primary: z.boolean().describe("Make this document the issue primary artifact.").optional(),
|
|
13718
13727
|
summary: z.string().describe("Optional version summary.").optional()
|
|
13719
13728
|
}),
|
|
13720
13729
|
validation: {
|
|
@@ -14359,8 +14368,6 @@ class DispatchClient {
|
|
|
14359
14368
|
return this.#json("POST", artifactPath, input);
|
|
14360
14369
|
const form = new FormData;
|
|
14361
14370
|
form.set("name", input.name);
|
|
14362
|
-
if (input.primary !== undefined)
|
|
14363
|
-
form.set("primary", String(input.primary));
|
|
14364
14371
|
if (input.summary !== undefined)
|
|
14365
14372
|
form.set("summary", input.summary);
|
|
14366
14373
|
if (input.actor !== undefined)
|
|
@@ -14879,20 +14886,17 @@ Open anchored asks/comments: ${marks.join(", ")}`,
|
|
|
14879
14886
|
};
|
|
14880
14887
|
}
|
|
14881
14888
|
case "dispatch_artifact": {
|
|
14882
|
-
const primary = optionalBoolean(args, "primary");
|
|
14883
14889
|
const summary = optionalString(args, "summary");
|
|
14884
14890
|
const name = stringArg(args, "name");
|
|
14885
14891
|
const content = optionalString(args, "content");
|
|
14886
14892
|
const result = await client.artifact(issue(), content === undefined ? {
|
|
14887
14893
|
name,
|
|
14888
14894
|
file: Bun.file(resolvePath(input.cwd, stringArg(args, "path"))),
|
|
14889
|
-
...primary === undefined ? {} : { primary },
|
|
14890
14895
|
...summary === undefined ? {} : { summary },
|
|
14891
14896
|
actor
|
|
14892
14897
|
} : {
|
|
14893
14898
|
name,
|
|
14894
14899
|
content,
|
|
14895
|
-
...primary === undefined ? {} : { primary },
|
|
14896
14900
|
...summary === undefined ? {} : { summary },
|
|
14897
14901
|
actor
|
|
14898
14902
|
});
|
package/package.json
CHANGED
package/skills/dispatch/SKILL.md
CHANGED
|
@@ -12,6 +12,38 @@ The server enforces high signal: an ask question is at most 800 characters with
|
|
|
12
12
|
options; comment and message bodies are at most 2,000 characters; an artifact is at most 25 MiB.
|
|
13
13
|
It refuses over-limit input; it never truncates it. GitHub threads and markers no longer exist.
|
|
14
14
|
|
|
15
|
+
## Writing a spec
|
|
16
|
+
|
|
17
|
+
A spec is a decision record for the human who decides and the implementer who builds, not a
|
|
18
|
+
transcript of your thinking. Use exactly these document headings in this order.
|
|
19
|
+
|
|
20
|
+
| Section | Required content | Form |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| **Decisions needed** | Only decisions requiring human authority, taste, or risk appetite. Each states one question, two or three options with tradeoffs, and a recommendation. Every item is an anchored `dispatch_ask`. Answered items move into Requirements with provenance, then leave this section. No other section asks the reader anything. Empty means `None.` | One decision per line; anchor each ask to that line. |
|
|
23
|
+
| **Acceptance** | Every outcome names its check and user-facing surface. An outcome without a verification method is not acceptance criteria. | Numbered lines; browser scenario, API call, or CLI command. |
|
|
24
|
+
| **Requirements** | Provenance is a verbatim human quote or `inferred: <reasoning>`; readers treat inferred requirements as hypotheses. Do not restate the prompt in prose. | `requirement \| provenance` table. |
|
|
25
|
+
| **Design** | State the files, components, routes, and data flow that change. | Facts, not narrative; diagrams only for genuine structure. |
|
|
26
|
+
| **Errors** | Name the behaviour for every error condition; never specify a silent fallback. | `condition \| behaviour` table. |
|
|
27
|
+
| **Testing** | Map every acceptance line to the proof that exercises it. | Suite or scenario. |
|
|
28
|
+
| **Rejected** | Record each considered alternative and why it was rejected so it is not proposed again. | One alternative per line. |
|
|
29
|
+
|
|
30
|
+
### Rules
|
|
31
|
+
|
|
32
|
+
- Every sentence is a fact, decision, or risk; delete the rest.
|
|
33
|
+
- Use tables over prose and keep one idea per line.
|
|
34
|
+
- Do not use Overview, Background, Introduction, Summary, or Conclusion sections.
|
|
35
|
+
- Do not hedge with “might” or “could consider.”
|
|
36
|
+
- Do not use TBD, TODO, or placeholders; an open item is a Decision needed.
|
|
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. The spec is the record; comments are the discussion.
|
|
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.
|
|
46
|
+
|
|
15
47
|
## Your issue
|
|
16
48
|
|
|
17
49
|
Every session works on an issue. Legion pre-fills `issue` from `LEGION_ISSUE`: use a native issue key
|
|
@@ -26,6 +58,7 @@ dispatch_issue({ project, title, parent?, external?, spec? })
|
|
|
26
58
|
```
|
|
27
59
|
It returns `details` `{ issue, topic }`. Use `dispatch_issue` only to create an issue; never use
|
|
28
60
|
it to park a question.
|
|
61
|
+
When `spec` is supplied, follow [Writing a spec](#writing-a-spec).
|
|
29
62
|
|
|
30
63
|
## Asking
|
|
31
64
|
|
|
@@ -43,8 +76,10 @@ dispatch_ask({
|
|
|
43
76
|
It returns `details` `{ issue, topic, ask }`. Options are buttons: never enumerate choices in
|
|
44
77
|
prose. Put the recommendation in `question`, and put each selectable choice in `options`.
|
|
45
78
|
Anchor a document question with `anchor: { artifact, quote, occurrence? }`; `occurrence` is
|
|
46
|
-
zero-based and selects a repeated quote.
|
|
47
|
-
|
|
79
|
+
zero-based and selects a repeated quote. The server writes the resulting mark. The HTTP API also
|
|
80
|
+
accepts `{ artifact, mark_id }` from a browser that has already written its mark; Dispatch tools
|
|
81
|
+
use the quote form. An anchor whose quote disappears becomes orphaned but remains readable against
|
|
82
|
+
its original document version.
|
|
48
83
|
|
|
49
84
|
An ask stays open until a human answers, unless its question no longer needs that answer. Retract a
|
|
50
85
|
moot or superseded question, or self-resolve one after finding the answer:
|
|
@@ -65,6 +100,7 @@ answer. Use `reply_to_ask` on `dispatch_comment` to reply under your own ask; it
|
|
|
65
100
|
exclusive with `reply_to`.
|
|
66
101
|
|
|
67
102
|
## The spec is where narrative goes
|
|
103
|
+
Write and update the issue specification according to [Writing a spec](#writing-a-spec).
|
|
68
104
|
|
|
69
105
|
Read the current document before changing it:
|
|
70
106
|
|
|
@@ -72,7 +108,7 @@ Read the current document before changing it:
|
|
|
72
108
|
dispatch_doc_read({ issue?, artifact?, version?, ref? })
|
|
73
109
|
```
|
|
74
110
|
It returns live or versioned markdown with open marks and `details` `{ issue }`; omit `artifact`
|
|
75
|
-
with `issue` to read the
|
|
111
|
+
with `issue` to read the issue specification. Then write narrative with:
|
|
76
112
|
|
|
77
113
|
```ts
|
|
78
114
|
dispatch_doc_edit({ issue, artifact, ops, summary? })
|
|
@@ -107,7 +143,8 @@ dispatch_comment({ issue, artifact?, quote?, occurrence?, body, reply_to?, reply
|
|
|
107
143
|
```
|
|
108
144
|
|
|
109
145
|
It returns `details` `{ issue, topic, comment }`. `quote` requires `artifact`; omit both for a
|
|
110
|
-
floating issue comment.
|
|
146
|
+
floating issue comment. A reply (`reply_to`/`reply_to_ask`) takes no `quote`; it belongs to its
|
|
147
|
+
parent's anchor. Use `reply_to` to continue a comment thread; use `reply_to_ask` to reply
|
|
111
148
|
directly under a question asked with `dispatch_ask`. The two are mutually exclusive.
|
|
112
149
|
|
|
113
150
|
Propose an exact replacement instead of describing it:
|
|
@@ -118,28 +155,31 @@ dispatch_suggest({ issue, artifact, quote, replace_with, body?, occurrence? })
|
|
|
118
155
|
|
|
119
156
|
It returns `details` `{ issue, topic, comment }`. A human accepts or rejects a suggestion. On
|
|
120
157
|
`TARGET_AMBIGUOUS`, add zero-based `occurrence`. On `TARGET_NOT_FOUND`, re-read the document
|
|
121
|
-
before retrying. `
|
|
122
|
-
`
|
|
123
|
-
|
|
158
|
+
before retrying. `INVALID_ANCHOR` requires exactly one nonempty anchor `quote` or `mark_id`;
|
|
159
|
+
`ANCHOR_MISSING` means a browser mark was not observed in the live tree, and
|
|
160
|
+
`ANCHOR_ORPHANED` means its marked text no longer exists. `INVALID_MARKDOWN` and `DOC_SCHEMA`
|
|
161
|
+
reject Markdown or a live tree outside the Proof schema. `INVALID_OP` names a malformed edit;
|
|
162
|
+
`CAP_EXCEEDED` never truncates; `ISSUE_CLOSED` rejects a write. `ACTOR_KIND` and `ROUTE_INVALID`
|
|
163
|
+
reject an invalid actor or route.
|
|
124
164
|
|
|
125
165
|
## Artifacts
|
|
126
166
|
|
|
127
167
|
Attach an image, diagram, or local file with:
|
|
128
168
|
|
|
129
169
|
```ts
|
|
130
|
-
dispatch_artifact({ issue, name, path,
|
|
170
|
+
dispatch_artifact({ issue, name, path, summary? })
|
|
131
171
|
```
|
|
132
172
|
|
|
133
173
|
Or, when the text is already in the call, post a Markdown document directly:
|
|
134
174
|
|
|
135
175
|
```ts
|
|
136
|
-
dispatch_artifact({ issue, name: "spec.md", content: "# Design\n..."
|
|
176
|
+
dispatch_artifact({ issue, name: "spec.md", content: "# Design\n..." })
|
|
137
177
|
```
|
|
138
178
|
|
|
139
179
|
Exactly one of `path` and `content` is required. The inline form sends JSON with
|
|
140
180
|
`Content-Type: application/json`. It returns `details` `{ issue, topic, artifact, version }`.
|
|
141
|
-
Uploading the same `name` creates its next version. Use `content`
|
|
142
|
-
|
|
181
|
+
Uploading the same `name` creates its next version. Use `content` when the text is already in
|
|
182
|
+
the call.
|
|
143
183
|
|
|
144
184
|
## Messages
|
|
145
185
|
|
|
@@ -70,13 +70,15 @@ exercise a criterion end to end, building that path is a child issue of this tre
|
|
|
70
70
|
relationship from `parent`. Keep the returned issue keys in ordered waves; a child is
|
|
71
71
|
inert until released.
|
|
72
72
|
|
|
73
|
+
Specifications written into Dispatch follow [`skills/dispatch`'s Writing a spec](../dispatch/SKILL.md#writing-a-spec).
|
|
74
|
+
|
|
73
75
|
Write one root specification containing the accepted scope, adoption/decomposition,
|
|
74
76
|
waves, acceptance criteria, and integration test. When the config-armed root design gate
|
|
75
77
|
applies, run this exact sequence **before any Legion-role spawn**, including a
|
|
76
78
|
sub-architect:
|
|
77
79
|
|
|
78
80
|
```text
|
|
79
|
-
dispatch_artifact({ issue: "<root issue>", name: "spec.md", content: "<root specification>",
|
|
81
|
+
dispatch_artifact({ issue: "<root issue>", name: "spec.md", content: "<root specification>", summary: "<one-line summary>" })
|
|
80
82
|
askId = dispatch_ask({
|
|
81
83
|
issue: "<root issue>",
|
|
82
84
|
question: "<specification summary and the decision requested>",
|
|
@@ -118,6 +118,8 @@ path-scoped workflow.
|
|
|
118
118
|
|
|
119
119
|
## Phase work
|
|
120
120
|
|
|
121
|
+
Specifications written into Dispatch follow [`skills/dispatch`'s Writing a spec](../dispatch/SKILL.md#writing-a-spec).
|
|
122
|
+
|
|
121
123
|
Follow the repository's normal engineering workflow and the assigned issue's acceptance
|
|
122
124
|
criteria. Your phase's own charter and the predecessor handoffs you read define the phase
|
|
123
125
|
artifact and its completion evidence. Do not replace architect-owned decomposition, gate
|