@sjawhar/opencode-legion-envoy 0.30.0 → 0.32.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 +32 -46
- package/package.json +1 -1
- package/skills/dispatch/SKILL.md +40 -7
- package/skills/github/SKILL.md +4 -4
- package/skills/legion-architect/SKILL.md +43 -41
- package/skills/legion-controller/SKILL.md +44 -43
- package/skills/legion-worker/SKILL.md +31 -23
- package/skills/linear/SKILL.md +1 -1
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: {
|
|
@@ -13874,8 +13883,17 @@ var legionRole = _enum2(LEGION_ROLES);
|
|
|
13874
13883
|
var requiredUnknown = unknown().refine((value) => value !== undefined, {
|
|
13875
13884
|
message: "Required"
|
|
13876
13885
|
});
|
|
13877
|
-
var
|
|
13878
|
-
|
|
13886
|
+
var LIFECYCLE_STATUSES = [
|
|
13887
|
+
"triage",
|
|
13888
|
+
"icebox",
|
|
13889
|
+
"backlog",
|
|
13890
|
+
"todo",
|
|
13891
|
+
"in_progress",
|
|
13892
|
+
"testing",
|
|
13893
|
+
"needs_review",
|
|
13894
|
+
"retro",
|
|
13895
|
+
"done"
|
|
13896
|
+
];
|
|
13879
13897
|
var architectCapability = strictObject({
|
|
13880
13898
|
tree: nonEmptyString,
|
|
13881
13899
|
sessionId: nonEmptyString,
|
|
@@ -13928,33 +13946,10 @@ var LegionDaemonApi = {
|
|
|
13928
13946
|
request: architectCapability.extend({ generation: number2().int() }),
|
|
13929
13947
|
response: object({})
|
|
13930
13948
|
},
|
|
13931
|
-
IssueCreate: {
|
|
13932
|
-
request: architectCapability.extend({
|
|
13933
|
-
title: nonEmptyString,
|
|
13934
|
-
body: nonEmptyString,
|
|
13935
|
-
labels: array(architectMutableLabel).optional()
|
|
13936
|
-
}),
|
|
13937
|
-
response: object({ issue: nonEmptyString, url: nonEmptyString })
|
|
13938
|
-
},
|
|
13939
13949
|
WaveRelease: {
|
|
13940
|
-
request: architectCapability.extend({
|
|
13950
|
+
request: architectCapability.extend({ issues: array(nonEmptyString).optional() }),
|
|
13941
13951
|
response: object({ released: array(nonEmptyString) })
|
|
13942
13952
|
},
|
|
13943
|
-
Comment: {
|
|
13944
|
-
request: architectCapability.extend({ issue: nonEmptyString, body: nonEmptyString }),
|
|
13945
|
-
response: object({ commentId: number2().int(), url: nonEmptyString })
|
|
13946
|
-
},
|
|
13947
|
-
PostBody: {
|
|
13948
|
-
request: architectCapability.extend({ issue: nonEmptyString, body: nonEmptyString }),
|
|
13949
|
-
response: object({})
|
|
13950
|
-
},
|
|
13951
|
-
Labels: {
|
|
13952
|
-
request: architectCapability.extend({
|
|
13953
|
-
issue: nonEmptyString,
|
|
13954
|
-
add: array(architectMutableLabel).optional()
|
|
13955
|
-
}),
|
|
13956
|
-
response: object({ labels: array(nonEmptyString) })
|
|
13957
|
-
},
|
|
13958
13953
|
Escalate: {
|
|
13959
13954
|
request: architectCapability.extend({
|
|
13960
13955
|
kind: _enum2(["re-file", "capacity", "cross-tree"]),
|
|
@@ -13962,10 +13957,6 @@ var LegionDaemonApi = {
|
|
|
13962
13957
|
}),
|
|
13963
13958
|
response: object({})
|
|
13964
13959
|
},
|
|
13965
|
-
IssueClose: {
|
|
13966
|
-
request: architectCapability.extend({ issue: nonEmptyString, comment: string2().optional() }),
|
|
13967
|
-
response: object({})
|
|
13968
|
-
},
|
|
13969
13960
|
ProvisioningCredential: {
|
|
13970
13961
|
request: architectCapability.extend({ issue: nonEmptyString }),
|
|
13971
13962
|
response: object({ token: nonEmptyString })
|
|
@@ -14028,16 +14019,16 @@ var LegionDaemonApi = {
|
|
|
14028
14019
|
secret: nonEmptyString
|
|
14029
14020
|
})
|
|
14030
14021
|
},
|
|
14031
|
-
|
|
14032
|
-
request: controllerIssue
|
|
14022
|
+
IssueStatus: {
|
|
14023
|
+
request: controllerIssue.extend({
|
|
14024
|
+
status: _enum2(LIFECYCLE_STATUSES),
|
|
14025
|
+
tree: nonEmptyString.optional(),
|
|
14026
|
+
sessionId: nonEmptyString.optional()
|
|
14027
|
+
}),
|
|
14033
14028
|
response: object({})
|
|
14034
14029
|
},
|
|
14035
|
-
|
|
14036
|
-
request:
|
|
14037
|
-
response: object({ result: _enum2(["spawned", "queued"]) })
|
|
14038
|
-
},
|
|
14039
|
-
Backlog: {
|
|
14040
|
-
request: controllerIssue.extend({ marker: nonEmptyString }),
|
|
14030
|
+
GatesRegister: {
|
|
14031
|
+
request: architectCapability.extend({ issue: nonEmptyString, askId: nonEmptyString }),
|
|
14041
14032
|
response: object({})
|
|
14042
14033
|
},
|
|
14043
14034
|
Grant: {
|
|
@@ -14377,8 +14368,6 @@ class DispatchClient {
|
|
|
14377
14368
|
return this.#json("POST", artifactPath, input);
|
|
14378
14369
|
const form = new FormData;
|
|
14379
14370
|
form.set("name", input.name);
|
|
14380
|
-
if (input.primary !== undefined)
|
|
14381
|
-
form.set("primary", String(input.primary));
|
|
14382
14371
|
if (input.summary !== undefined)
|
|
14383
14372
|
form.set("summary", input.summary);
|
|
14384
14373
|
if (input.actor !== undefined)
|
|
@@ -14897,20 +14886,17 @@ Open anchored asks/comments: ${marks.join(", ")}`,
|
|
|
14897
14886
|
};
|
|
14898
14887
|
}
|
|
14899
14888
|
case "dispatch_artifact": {
|
|
14900
|
-
const primary = optionalBoolean(args, "primary");
|
|
14901
14889
|
const summary = optionalString(args, "summary");
|
|
14902
14890
|
const name = stringArg(args, "name");
|
|
14903
14891
|
const content = optionalString(args, "content");
|
|
14904
14892
|
const result = await client.artifact(issue(), content === undefined ? {
|
|
14905
14893
|
name,
|
|
14906
14894
|
file: Bun.file(resolvePath(input.cwd, stringArg(args, "path"))),
|
|
14907
|
-
...primary === undefined ? {} : { primary },
|
|
14908
14895
|
...summary === undefined ? {} : { summary },
|
|
14909
14896
|
actor
|
|
14910
14897
|
} : {
|
|
14911
14898
|
name,
|
|
14912
14899
|
content,
|
|
14913
|
-
...primary === undefined ? {} : { primary },
|
|
14914
14900
|
...summary === undefined ? {} : { summary },
|
|
14915
14901
|
actor
|
|
14916
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
|
|
|
@@ -65,6 +98,7 @@ answer. Use `reply_to_ask` on `dispatch_comment` to reply under your own ask; it
|
|
|
65
98
|
exclusive with `reply_to`.
|
|
66
99
|
|
|
67
100
|
## The spec is where narrative goes
|
|
101
|
+
Write and update the issue specification according to [Writing a spec](#writing-a-spec).
|
|
68
102
|
|
|
69
103
|
Read the current document before changing it:
|
|
70
104
|
|
|
@@ -72,7 +106,7 @@ Read the current document before changing it:
|
|
|
72
106
|
dispatch_doc_read({ issue?, artifact?, version?, ref? })
|
|
73
107
|
```
|
|
74
108
|
It returns live or versioned markdown with open marks and `details` `{ issue }`; omit `artifact`
|
|
75
|
-
with `issue` to read the
|
|
109
|
+
with `issue` to read the issue specification. Then write narrative with:
|
|
76
110
|
|
|
77
111
|
```ts
|
|
78
112
|
dispatch_doc_edit({ issue, artifact, ops, summary? })
|
|
@@ -119,27 +153,26 @@ dispatch_suggest({ issue, artifact, quote, replace_with, body?, occurrence? })
|
|
|
119
153
|
It returns `details` `{ issue, topic, comment }`. A human accepts or rejects a suggestion. On
|
|
120
154
|
`TARGET_AMBIGUOUS`, add zero-based `occurrence`. On `TARGET_NOT_FOUND`, re-read the document
|
|
121
155
|
before retrying. `INVALID_OP` names a malformed edit; `CAP_EXCEEDED` never truncates;
|
|
122
|
-
`ISSUE_CLOSED` rejects a write. `ACTOR_KIND
|
|
123
|
-
invalid actor, route, or primary artifact.
|
|
156
|
+
`ISSUE_CLOSED` rejects a write. `ACTOR_KIND` and `ROUTE_INVALID` reject an invalid actor or route.
|
|
124
157
|
|
|
125
158
|
## Artifacts
|
|
126
159
|
|
|
127
160
|
Attach an image, diagram, or local file with:
|
|
128
161
|
|
|
129
162
|
```ts
|
|
130
|
-
dispatch_artifact({ issue, name, path,
|
|
163
|
+
dispatch_artifact({ issue, name, path, summary? })
|
|
131
164
|
```
|
|
132
165
|
|
|
133
166
|
Or, when the text is already in the call, post a Markdown document directly:
|
|
134
167
|
|
|
135
168
|
```ts
|
|
136
|
-
dispatch_artifact({ issue, name: "spec.md", content: "# Design\n..."
|
|
169
|
+
dispatch_artifact({ issue, name: "spec.md", content: "# Design\n..." })
|
|
137
170
|
```
|
|
138
171
|
|
|
139
172
|
Exactly one of `path` and `content` is required. The inline form sends JSON with
|
|
140
173
|
`Content-Type: application/json`. It returns `details` `{ issue, topic, artifact, version }`.
|
|
141
|
-
Uploading the same `name` creates its next version. Use `content`
|
|
142
|
-
|
|
174
|
+
Uploading the same `name` creates its next version. Use `content` when the text is already in
|
|
175
|
+
the call.
|
|
143
176
|
|
|
144
177
|
## Messages
|
|
145
178
|
|
package/skills/github/SKILL.md
CHANGED
|
@@ -104,7 +104,7 @@ gh api graphql -f query='query {
|
|
|
104
104
|
Add a label to an issue (additive — does not remove existing labels):
|
|
105
105
|
|
|
106
106
|
```bash
|
|
107
|
-
gh issue edit $ISSUE_NUMBER --add-label "
|
|
107
|
+
gh issue edit $ISSUE_NUMBER --add-label "bug" -R $OWNER/$REPO
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
**Parameters:**
|
|
@@ -114,7 +114,7 @@ gh issue edit $ISSUE_NUMBER --add-label "needs-approval" -R $OWNER/$REPO
|
|
|
114
114
|
|
|
115
115
|
**Example:**
|
|
116
116
|
```bash
|
|
117
|
-
gh issue edit 123 --add-label "
|
|
117
|
+
gh issue edit 123 --add-label "bug" -R acme/backend
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
### Remove Label
|
|
@@ -122,7 +122,7 @@ gh issue edit 123 --add-label "needs-approval" -R acme/backend
|
|
|
122
122
|
Remove a label from an issue:
|
|
123
123
|
|
|
124
124
|
```bash
|
|
125
|
-
gh issue edit $ISSUE_NUMBER --remove-label "
|
|
125
|
+
gh issue edit $ISSUE_NUMBER --remove-label "wontfix" -R $OWNER/$REPO
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
**Parameters:**
|
|
@@ -132,7 +132,7 @@ gh issue edit $ISSUE_NUMBER --remove-label "needs-approval" -R $OWNER/$REPO
|
|
|
132
132
|
|
|
133
133
|
**Example:**
|
|
134
134
|
```bash
|
|
135
|
-
gh issue edit 123 --remove-label "
|
|
135
|
+
gh issue edit 123 --remove-label "wontfix" -R acme/backend
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
### Comment on Issue
|
|
@@ -10,29 +10,31 @@ with human-created children; either way you own its complete outcome. Work from
|
|
|
10
10
|
wakes and current artifacts. Do not perform code work yourself and do not rely on a
|
|
11
11
|
separate coordinator to finish necessary work.
|
|
12
12
|
|
|
13
|
+
This skill documents the target Dispatch-native contract. The `legion` tool's
|
|
14
|
+
`register_gate`/`release_wave`/`set_status` ops, the Dispatch key format, and the
|
|
15
|
+
`dispatch_*` tool family land with PR B (#TBD); until that PR merges, this skill's contract
|
|
16
|
+
is not yet runnable on `main`.
|
|
17
|
+
|
|
13
18
|
## Tool and ownership boundaries
|
|
14
19
|
|
|
15
|
-
- Use the `legion` tool for lifecycle writes. Its issue key
|
|
16
|
-
`
|
|
20
|
+
- Use the `legion` tool for lifecycle writes. Its issue key is the Dispatch key
|
|
21
|
+
(pattern `^[A-Z][A-Z0-9]*-[0-9]+$`, e.g. `LEGION-41`).
|
|
17
22
|
- Use `legion({ op: "spawn_worker", issue, role, task })` for every Legion role spawn.
|
|
18
23
|
Message a known phase worker with `envoy_publish` to `notifications.role.` followed by
|
|
19
24
|
its encoded role token (the token `spawn_worker` returned for it); re-assign it by
|
|
20
25
|
calling `spawn_worker` again on the same existing role, which resumes the same process
|
|
21
26
|
instead of starting a fresh one. Phase workers escalate lifecycle, scope, and
|
|
22
27
|
cross-phase matters the same way: `envoy_publish` to your own encoded token. Any role
|
|
23
|
-
may use `
|
|
28
|
+
may use `dispatch_ask` directly for a standalone human question; replies return to the
|
|
24
29
|
asking session.
|
|
25
30
|
- The daemon spawns each role as its own process with the issue's context already in its
|
|
26
31
|
environment. Never hand-format a role token: the daemon encodes one as
|
|
27
|
-
`legion-<project>-<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
- Use only the live label vocabulary: `needs-approval`, `human-approved`,
|
|
34
|
-
`legion-child`, and `legion-backlog`. Do not attempt to apply a label whose ownership
|
|
35
|
-
belongs to the controller or Sami.
|
|
32
|
+
`legion-<project>-<KEY>-<role>`; for example, project `acme`, issue `LEGION-41`, role
|
|
33
|
+
`architect` encodes to `legion-acme-LEGION-41-architect`. Reuse a token you already
|
|
34
|
+
hold (your own, or one `spawn_worker` returned) or compute another with the
|
|
35
|
+
`roleToken` helper from `@legion/contracts` exactly the way the daemon does.
|
|
36
|
+
- There is no label vocabulary. Dispatch status replaces the board, and the design gate
|
|
37
|
+
is a `dispatch_ask` answered `Approve`, not a label. Never attempt to apply a label.
|
|
36
38
|
- Deferring necessary work is failure. The sole valid deferral is a new child issue you
|
|
37
39
|
create and continue to own. Re-file a genuinely independent child through the
|
|
38
40
|
controller rather than treating it as an abandoned dependency.
|
|
@@ -46,7 +48,7 @@ exercise a criterion end to end, building that path is a child issue of this tre
|
|
|
46
48
|
|
|
47
49
|
- **Existing children:** adopt them. Do not replace or re-decompose human-created work.
|
|
48
50
|
Put every adopted child into the initial wave. **You MUST call**
|
|
49
|
-
`legion({ op: "
|
|
51
|
+
`legion({ op: "release_wave", issues: ["LEGION-41", "LEGION-42"] })`
|
|
50
52
|
**before any `spawn_worker` call for an adopted child.** Until release, the daemon
|
|
51
53
|
holds that child's role activity. Then spawn each child's daemon-managed sub-architect
|
|
52
54
|
owner.
|
|
@@ -54,16 +56,21 @@ exercise a criterion end to end, building that path is a child issue of this tre
|
|
|
54
56
|
completed and integrated as one unit. Otherwise create complete child issues with:
|
|
55
57
|
|
|
56
58
|
```text
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
dispatch_issue({
|
|
60
|
+
project: "<project>",
|
|
61
|
+
parent: "<root issue>",
|
|
59
62
|
title: "<child outcome>",
|
|
60
|
-
|
|
61
|
-
labels: []
|
|
63
|
+
spec: "<acceptance criteria, scope, and context>"
|
|
62
64
|
})
|
|
63
65
|
```
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
the
|
|
67
|
+
`project` is the issue key's prefix before `-<n>` (e.g. `LEGSMOKE-3` → `LEGSMOKE`) — not
|
|
68
|
+
the role-token `<project>` (the daemon's own project, e.g. `acme`), a different string. A
|
|
69
|
+
root session has `LEGION_TREE == LEGION_ISSUE`. The daemon establishes the sub-issue
|
|
70
|
+
relationship from `parent`. Keep the returned issue keys in ordered waves; a child is
|
|
71
|
+
inert until released.
|
|
72
|
+
|
|
73
|
+
Specifications written into Dispatch follow [`skills/dispatch`'s Writing a spec](../dispatch/SKILL.md#writing-a-spec).
|
|
67
74
|
|
|
68
75
|
Write one root specification containing the accepted scope, adoption/decomposition,
|
|
69
76
|
waves, acceptance criteria, and integration test. When the config-armed root design gate
|
|
@@ -71,20 +78,18 @@ applies, run this exact sequence **before any Legion-role spawn**, including a
|
|
|
71
78
|
sub-architect:
|
|
72
79
|
|
|
73
80
|
```text
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
context: "<what the tree is, what triggered the gate>",
|
|
80
|
-
question: "<specification summary and the decision requested>"
|
|
81
|
+
dispatch_artifact({ issue: "<root issue>", name: "spec.md", content: "<root specification>", summary: "<one-line summary>" })
|
|
82
|
+
askId = dispatch_ask({
|
|
83
|
+
issue: "<root issue>",
|
|
84
|
+
question: "<specification summary and the decision requested>",
|
|
85
|
+
options: [{ label: "Approve" }, ...]
|
|
81
86
|
})
|
|
87
|
+
legion({ op: "register_gate", issue: "<root issue>", askId })
|
|
82
88
|
```
|
|
83
89
|
|
|
84
90
|
Then park. Do not release a wave or spawn a Legion role until a later delivered wake
|
|
85
|
-
shows `
|
|
86
|
-
|
|
87
|
-
this sequence.
|
|
91
|
+
shows `design-approved` on the root. Approval covers the entire tree: later waves,
|
|
92
|
+
re-scopes, and integration-failure children do not repeat this sequence.
|
|
88
93
|
|
|
89
94
|
## 2. Children in flight
|
|
90
95
|
|
|
@@ -92,7 +97,7 @@ Release only the next useful wave, then give its owners their work. A release is
|
|
|
92
97
|
explicit lifecycle write:
|
|
93
98
|
|
|
94
99
|
```text
|
|
95
|
-
legion({ op: "
|
|
100
|
+
legion({ op: "release_wave", issues: ["LEGION-41", "LEGION-42"] })
|
|
96
101
|
```
|
|
97
102
|
|
|
98
103
|
After release, spawn each relevant owner; for example:
|
|
@@ -100,7 +105,7 @@ After release, spawn each relevant owner; for example:
|
|
|
100
105
|
```text
|
|
101
106
|
legion({
|
|
102
107
|
op: "spawn_worker",
|
|
103
|
-
issue: "
|
|
108
|
+
issue: "LEGION-41",
|
|
104
109
|
role: "architect",
|
|
105
110
|
task: "Own this child through its lifecycle and report its evidence."
|
|
106
111
|
})
|
|
@@ -122,7 +127,7 @@ and current `main` integration surface:
|
|
|
122
127
|
```text
|
|
123
128
|
legion({
|
|
124
129
|
op: "spawn_worker",
|
|
125
|
-
issue: "
|
|
130
|
+
issue: "LEGION-40",
|
|
126
131
|
role: "tester",
|
|
127
132
|
task: "Verify this parent issue against its acceptance criteria on current main; return reproducible integration evidence."
|
|
128
133
|
})
|
|
@@ -181,15 +186,12 @@ If anything changes the approved head, return to review; do not let the merger p
|
|
|
181
186
|
|
|
182
187
|
## 7. Close
|
|
183
188
|
|
|
184
|
-
After the merge result and sign-off are recorded, close this issue
|
|
185
|
-
|
|
189
|
+
After the merge result and sign-off are recorded, post the sign-off and close this issue
|
|
190
|
+
through the Legion write surface:
|
|
186
191
|
|
|
187
192
|
```text
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
issue: "owner/repo#40",
|
|
191
|
-
comment: "<sign-off: scope, integration evidence, review, retro, Sami approval, and merge>"
|
|
192
|
-
})
|
|
193
|
+
dispatch_comment({ issue: "LEGION-40", body: "<sign-off: scope, integration evidence, review, retro, Sami approval, and merge>" })
|
|
194
|
+
legion({ op: "set_status", issue: "LEGION-40", status: "done" })
|
|
193
195
|
```
|
|
194
196
|
|
|
195
197
|
Closing a child supplies the closure event to its parent. Do not close a parent until the
|
|
@@ -220,6 +222,6 @@ corresponding lifecycle procedure.
|
|
|
220
222
|
|
|
221
223
|
Controller-actionable matters are exactly re-filing a genuinely independent child,
|
|
222
224
|
capacity, and cross-tree conflict. Use the Legion escalation operation for those. Handle
|
|
223
|
-
everything else in the tree, or use `
|
|
224
|
-
Sami directly with `
|
|
225
|
+
everything else in the tree, or use `dispatch_ask` for a human question; workers may reach
|
|
226
|
+
Sami directly with `dispatch_ask` the same way. Do not create a wait loop for any wake
|
|
225
227
|
source.
|
|
@@ -9,6 +9,10 @@ The controller is the one persistent, wake-driven session for a Legion project.
|
|
|
9
9
|
triage, escalation, and human-interaction judgments; it never does phase-worker work or
|
|
10
10
|
routes raw events into an architect.
|
|
11
11
|
|
|
12
|
+
This skill documents the target Dispatch-native contract. The `legion` tool's `set_status`
|
|
13
|
+
op, `dispatch_read`/`dispatch_issue`, and the Dispatch key format land with PR B (#TBD);
|
|
14
|
+
until that PR merges, this skill's contract is not yet runnable on `main`.
|
|
15
|
+
|
|
12
16
|
## Start and claim the controller role
|
|
13
17
|
|
|
14
18
|
The Legion extension claims `legion-<project>-controller` and registers controller readiness
|
|
@@ -23,11 +27,11 @@ For an interactive takeover, start OMP with `LEGION_CONTROLLER_SECRET` and
|
|
|
23
27
|
|
|
24
28
|
The command resolves the project from daemon state, claims the Envoy role for the current
|
|
25
29
|
session, and posts readiness before controller commands can act. It retains the environment
|
|
26
|
-
capability for `legion
|
|
30
|
+
capability for `legion({ op: "set_status", issue, status })`. Never pass a secret as a command argument
|
|
27
31
|
or copy it into a transcript.
|
|
28
32
|
|
|
29
33
|
This handshake lets the daemon redeliver held controller work. It does not turn the controller
|
|
30
|
-
into a state holder: daemon state and
|
|
34
|
+
into a state holder: daemon state and the Dispatch project remain authoritative.
|
|
31
35
|
|
|
32
36
|
## Turn discipline
|
|
33
37
|
|
|
@@ -36,7 +40,7 @@ into a state holder: daemon state and GitHub artifacts remain authoritative.
|
|
|
36
40
|
- **One wake = one turn.** Handle exactly the wake's implication, then end the turn. Never
|
|
37
41
|
poll, idle-loop, or wait for another event.
|
|
38
42
|
- **Wakes are advisory.** Before any side effect, verify the current daemon state and the
|
|
39
|
-
relevant
|
|
43
|
+
relevant Dispatch issue. A stale or duplicate wake may cost a read, never a wrong action.
|
|
40
44
|
- **Controller state is disposable.** Do not reconstruct or preserve local controller
|
|
41
45
|
bookkeeping between turns.
|
|
42
46
|
|
|
@@ -44,52 +48,59 @@ into a state holder: daemon state and GitHub artifacts remain authoritative.
|
|
|
44
48
|
|
|
45
49
|
| Wake | Content | Controller action |
|
|
46
50
|
|---|---|---|
|
|
47
|
-
| New issue
|
|
48
|
-
| Backlog eligibility | slot freed / priority change | Reconsider parked items
|
|
49
|
-
| Architect escalation (controller-actionable only: re-file a child as a root issue, capacity, cross-tree conflicts) | request + context | Judge and act; issue-scoped human Q&A goes through `
|
|
50
|
-
| Resync report | artifact-driven anomaly list (zero-owner trees,
|
|
51
|
-
|
|
|
52
|
-
|
|
|
51
|
+
| New issue created in the Dispatch project (`issue.created`, status `triage`; resync heals misses) | issue key + triage context (incl. pre-existing children) | Triage: `legion({ op: "set_status", issue, status: "todo" })` to admit, or set `backlog`/`icebox` to park |
|
|
52
|
+
| Backlog eligibility | slot freed / priority change | Reconsider parked items and move the eligible root to `todo` |
|
|
53
|
+
| Architect escalation (controller-actionable only: re-file a child as a root issue, capacity, cross-tree conflicts) | request + context | Judge and act; issue-scoped human Q&A goes through `dispatch_ask` from the owning architect, not here |
|
|
54
|
+
| Resync report | artifact-driven anomaly list (zero-owner trees, untriaged-open, launch-failed) | Verify against fresh state, then heal |
|
|
55
|
+
| `child-status` | child key + status transition | Not controller-actionable by default; if the daemon could not route it to the parent's architect role, verify the transition and forward it with `envoy_publish` |
|
|
56
|
+
| Mention | Slack/GitHub PR @mention text | Answer, or route to the owning issue's architect role |
|
|
57
|
+
| Closed-tree activity (comment, review, CI on a closed tree) | issue, root, event summary | Read the artifact; if work should resume, `legion({ op: "set_status", issue: root, status: "todo" })`; otherwise no action — the event is not held or redelivered |
|
|
53
58
|
| Direct user message | — | Always first |
|
|
54
59
|
|
|
55
60
|
## New issue triage
|
|
56
61
|
|
|
57
|
-
1. Read `legion state --json`, then inspect the reported
|
|
58
|
-
Verify the issue is
|
|
59
|
-
has pre-existing children.
|
|
62
|
+
1. Read `legion state --json`, then inspect the reported Dispatch issue with `dispatch_read`.
|
|
63
|
+
Verify the issue is in this project, is eligible for a root process, and whether it
|
|
64
|
+
has pre-existing children. Dispatch and daemon state, not the wake text, decide triage.
|
|
60
65
|
2. If it should run now, admit the root issue:
|
|
61
66
|
|
|
62
|
-
```
|
|
63
|
-
legion
|
|
67
|
+
```text
|
|
68
|
+
legion({ op: "set_status", issue: "<issue>", status: "todo" })
|
|
64
69
|
```
|
|
65
70
|
|
|
66
|
-
3. If it should deliberately wait,
|
|
71
|
+
3. If it should deliberately wait, move it to a parked status instead of leaving it in
|
|
72
|
+
`triage`:
|
|
67
73
|
|
|
68
|
-
```
|
|
69
|
-
legion
|
|
74
|
+
```text
|
|
75
|
+
legion({ op: "set_status", issue: "<issue>", status: "backlog" })
|
|
70
76
|
```
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
(or `status: "icebox"` for longer-term deferral). Dispatch status is the durable record;
|
|
79
|
+
there is no separate marker to maintain. Do not triage a system-created child as a root
|
|
80
|
+
issue.
|
|
74
81
|
|
|
75
82
|
## Backlog eligibility
|
|
76
83
|
|
|
77
|
-
When a slot frees or priority changes, use `legion state --json` and the current
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
is a deliberate controller decision, not a no-op.
|
|
84
|
+
When a slot frees or priority changes, use `legion state --json` and the current Dispatch
|
|
85
|
+
issue to reconsider parked roots. Admit the selected root with
|
|
86
|
+
`legion({ op: "set_status", issue, status: "todo" })`. Moving an item to or from `backlog`/
|
|
87
|
+
`icebox` is a deliberate controller decision, not a no-op.
|
|
81
88
|
|
|
82
89
|
## Architect escalation
|
|
83
90
|
|
|
84
91
|
Only decide controller-actionable escalations: re-filing independent work, capacity, and
|
|
85
|
-
cross-tree conflicts. Issue-scoped human Q&A goes through `
|
|
92
|
+
cross-tree conflicts. Issue-scoped human Q&A goes through `dispatch_ask` from the owning
|
|
86
93
|
architect, not the controller.
|
|
87
94
|
|
|
88
|
-
For an independence judgment, verify the child and its parent against
|
|
89
|
-
|
|
95
|
+
For an independence judgment, verify the child and its parent against current daemon state
|
|
96
|
+
and the Dispatch issue. If the work belongs in an independent root:
|
|
90
97
|
|
|
91
|
-
1. File a **fresh root issue** with `
|
|
92
|
-
|
|
98
|
+
1. File a **fresh root issue** with `dispatch_issue({ project, title, spec })` (no `parent`).
|
|
99
|
+
`project` is the issue key's prefix before `-<n>` (e.g. `LEGSMOKE-3` → `LEGSMOKE`) — not
|
|
100
|
+
the role-token `<project>` (the daemon's own project, e.g. `acme`), a different string.
|
|
101
|
+
2. Park the child (`legion({ op: "set_status", issue: child, status: "icebox" })`) and leave
|
|
102
|
+
a pointer to the new root issue. The controller's capability is `todo`/`backlog`/`icebox`
|
|
103
|
+
only — only the owning architect or the daemon closes an issue as `done`.
|
|
93
104
|
3. Admit or deliberately backlog the new root through the normal triage procedure.
|
|
94
105
|
|
|
95
106
|
Never promote a child in place. Resolve capacity and cross-tree conflicts from verified
|
|
@@ -98,11 +109,11 @@ judgments.
|
|
|
98
109
|
|
|
99
110
|
## Resync report
|
|
100
111
|
|
|
101
|
-
Treat a resync report as an anomaly list, not an instruction. For every zero-owner tree
|
|
102
|
-
|
|
103
|
-
Then heal the verified condition: admit an eligible root,
|
|
104
|
-
|
|
105
|
-
until their source artifact explains the anomaly.
|
|
112
|
+
Treat a resync report as an anomaly list, not an instruction. For every zero-owner tree,
|
|
113
|
+
untriaged-open, or launch-failed issue it names, verify `legion state --json` and the
|
|
114
|
+
current Dispatch issue first. Then heal the verified condition: admit an eligible root, move
|
|
115
|
+
an issue back to its intended status, or use the applicable daemon control path. Do not act
|
|
116
|
+
on stale entries until their source artifact explains the anomaly.
|
|
106
117
|
|
|
107
118
|
## Mentions
|
|
108
119
|
|
|
@@ -111,13 +122,3 @@ human-facing information. Otherwise resolve the authoritative owning architect r
|
|
|
111
122
|
route the verified context with `envoy_publish`. Do not route raw event traffic or invent a
|
|
112
123
|
role token from a partial issue reference.
|
|
113
124
|
|
|
114
|
-
## Label vocabulary
|
|
115
|
-
|
|
116
|
-
Use only the project labels below, with their stated ownership:
|
|
117
|
-
|
|
118
|
-
| Label | Applied by | Removed by | Meaning |
|
|
119
|
-
|---|---|---|---|
|
|
120
|
-
| `needs-approval` | architect | controller/Sami when applying `human-approved` | design gate armed, awaiting Sami |
|
|
121
|
-
| `human-approved` | Sami or controller | Sami | design gate open |
|
|
122
|
-
| `legion-child` | daemon | never | system-created child |
|
|
123
|
-
| `legion-backlog` | controller | controller | deliberately unowned root |
|
|
@@ -11,6 +11,10 @@ phase gets its own long-lived process against the same jj workspace, run in turn
|
|
|
11
11
|
the phase assigned to you, report its completion to the architect, and leave the durable
|
|
12
12
|
copy the next phase can trust.
|
|
13
13
|
|
|
14
|
+
This skill documents the target Dispatch-native contract. `LEGION_ISSUE` as a Dispatch key,
|
|
15
|
+
the `dispatch_ask` tool, and the `Dispatch: <KEY>` PR-body linkage land with PR B (#TBD);
|
|
16
|
+
until that PR merges, this skill's contract is not yet runnable on `main`.
|
|
17
|
+
|
|
14
18
|
## Identity, scope, and role
|
|
15
19
|
|
|
16
20
|
The daemon spawns you as a separate `omp --mode rpc` process (behind `legion worker-shim`,
|
|
@@ -21,13 +25,11 @@ completes the boot handshake for you at session start — it registers with the
|
|
|
21
25
|
your role, and signals readiness. You never call `envoy_role_set` yourself.
|
|
22
26
|
|
|
23
27
|
Your role token is not the issue key spelled out literally. The daemon encodes it as
|
|
24
|
-
`legion-<project>-<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
prompt (a "Legion addressing" line the daemon appends), a sibling role's topic is yours
|
|
30
|
-
with the trailing `-<role>` replaced, and the `roleToken` helper in `@legion/contracts`
|
|
28
|
+
`legion-<project>-<KEY>-<role>`. For example, project `acme`, issue `LEGION-41`, role
|
|
29
|
+
`architect` encodes to `legion-acme-LEGION-41-architect`. Never hand-format one for another
|
|
30
|
+
role: your own role topic and your tree's architect's topic are stated at the end of your
|
|
31
|
+
system prompt (a "Legion addressing" line the daemon appends), a sibling role's topic is
|
|
32
|
+
yours with the trailing `-<role>` replaced, and the `roleToken` helper in `@legion/contracts`
|
|
31
33
|
computes any other one exactly the way the daemon does — prefer a topic you've already
|
|
32
34
|
been given before recomputing one.
|
|
33
35
|
|
|
@@ -47,7 +49,7 @@ Escalate a product, scope, cross-phase, or lifecycle decision to the owning arch
|
|
|
47
49
|
`envoy_publish` to its role topic (`notifications.role.` followed by its encoded token, see
|
|
48
50
|
above), carrying the verified facts and the decision needed. `hub` only reaches subagents
|
|
49
51
|
inside your own process, not the architect's separate one. For a durable question that needs
|
|
50
|
-
Sami directly, you may use
|
|
52
|
+
Sami directly, you may use `dispatch_ask` yourself; replies return to your own
|
|
51
53
|
session.
|
|
52
54
|
|
|
53
55
|
Because the same agent is always resumed for its phase, you may receive more than one
|
|
@@ -116,6 +118,8 @@ path-scoped workflow.
|
|
|
116
118
|
|
|
117
119
|
## Phase work
|
|
118
120
|
|
|
121
|
+
Specifications written into Dispatch follow [`skills/dispatch`'s Writing a spec](../dispatch/SKILL.md#writing-a-spec).
|
|
122
|
+
|
|
119
123
|
Follow the repository's normal engineering workflow and the assigned issue's acceptance
|
|
120
124
|
criteria. Your phase's own charter and the predecessor handoffs you read define the phase
|
|
121
125
|
artifact and its completion evidence. Do not replace architect-owned decomposition, gate
|
|
@@ -133,11 +137,13 @@ capability it needs; invoke GitHub through the credential helper:
|
|
|
133
137
|
legion gh -- <gh args…>
|
|
134
138
|
```
|
|
135
139
|
|
|
136
|
-
## GitHub comment attribution
|
|
140
|
+
## GitHub PR comment attribution
|
|
137
141
|
|
|
138
|
-
Append this exact structured footer to **every**
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
Append this exact structured footer to **every** pull-request comment and review that this
|
|
143
|
+
phase posts on GitHub. It preserves session provenance on the artifact itself so work stays
|
|
144
|
+
attributable to the session that produced it. Dispatch comments carry session provenance
|
|
145
|
+
natively through their own `actor`/`origin` fields; this footer is only for GitHub PR
|
|
146
|
+
artifacts:
|
|
141
147
|
|
|
142
148
|
```html
|
|
143
149
|
<!-- legion: {"session":"<session-id>","phase":"<phase>"} -->
|
|
@@ -146,7 +152,7 @@ itself so work stays attributable to the session that produced it:
|
|
|
146
152
|
For example:
|
|
147
153
|
|
|
148
154
|
```bash
|
|
149
|
-
legion gh --
|
|
155
|
+
legion gh -- pr comment <pr-number> \
|
|
150
156
|
--body $'Verification complete.\n\n<!-- legion: {"session":"<session-id>","phase":"<phase>"} -->' \
|
|
151
157
|
--repo <owner>/<repo>
|
|
152
158
|
```
|
|
@@ -159,17 +165,19 @@ procedure:
|
|
|
159
165
|
|
|
160
166
|
```bash
|
|
161
167
|
cd -- "$LEGION_WORKSPACE" && \
|
|
162
|
-
jj -R "$LEGION_WORKSPACE" bookmark set legion
|
|
163
|
-
jj -R "$LEGION_WORKSPACE" git push --bookmark legion
|
|
168
|
+
jj -R "$LEGION_WORKSPACE" bookmark set legion/<KEY> && \
|
|
169
|
+
jj -R "$LEGION_WORKSPACE" git push --bookmark legion/<KEY>
|
|
164
170
|
```
|
|
165
171
|
|
|
166
172
|
The provisioned issue workspace configures `credential.helper` with the daemon's absolute
|
|
167
173
|
credential command, so `jj -R "$LEGION_WORKSPACE" git push` authenticates transparently
|
|
168
174
|
through the same session capability. Never handle a token.
|
|
169
175
|
|
|
170
|
-
Then open the pull request with `legion gh -- pr create`. The
|
|
171
|
-
|
|
172
|
-
|
|
176
|
+
Then open the pull request with `legion gh -- pr create`. The PR body **must** contain the
|
|
177
|
+
line `Dispatch: <KEY>` — the daemon's fallback link from a PR to its Dispatch issue when the
|
|
178
|
+
branch name alone is ambiguous. The credential helper and `legion gh` provide the GitHub
|
|
179
|
+
identity; never export, fetch, or replace a token. Other phases advance the existing branch
|
|
180
|
+
rather than creating a replacement bookmark or PR.
|
|
173
181
|
|
|
174
182
|
## PR body and merge-queue discipline
|
|
175
183
|
|
|
@@ -243,13 +251,13 @@ cd -- "$LEGION_WORKSPACE" && \
|
|
|
243
251
|
```
|
|
244
252
|
|
|
245
253
|
If the issue bookmark exists locally, advance it and push it with the provisioned
|
|
246
|
-
credential helper. `--
|
|
247
|
-
first push:
|
|
254
|
+
credential helper. `--bookmark` also publishes the locally provisioned bookmark on its
|
|
255
|
+
first push — a bookmark not yet tracking a remote one is tracked automatically:
|
|
248
256
|
|
|
249
257
|
```bash
|
|
250
258
|
cd -- "$LEGION_WORKSPACE" && \
|
|
251
|
-
jj -R "$LEGION_WORKSPACE" bookmark set legion
|
|
252
|
-
jj -R "$LEGION_WORKSPACE" git push --bookmark legion
|
|
259
|
+
jj -R "$LEGION_WORKSPACE" bookmark set legion/<KEY> && \
|
|
260
|
+
jj -R "$LEGION_WORKSPACE" git push --bookmark legion/<KEY>
|
|
253
261
|
```
|
|
254
262
|
|
|
255
263
|
Do not report phase completion until the write, existence check, and handoff commit
|
|
@@ -279,5 +287,5 @@ session, if this phase's work needs to run again.
|
|
|
279
287
|
|
|
280
288
|
When blocked on lifecycle, scope, or cross-phase matters, `envoy_publish` the owning
|
|
281
289
|
architect a concise message: issue, phase, verified observation, what you tried, and the
|
|
282
|
-
decision required. Reach for `
|
|
290
|
+
decision required. Reach for `dispatch_ask` yourself only for a standalone human question
|
|
283
291
|
outside that coordination.
|
package/skills/linear/SKILL.md
CHANGED
|
@@ -39,7 +39,7 @@ Returns: title, description, status, labels, comments, attachments.
|
|
|
39
39
|
linear_linear(action="update", id="ABC-123", state="Done")
|
|
40
40
|
linear_linear(action="update", id="ABC-123", priority=1)
|
|
41
41
|
linear_linear(action="update", id="ABC-123", assignee="me")
|
|
42
|
-
linear_linear(action="update", id="ABC-123", labels=["
|
|
42
|
+
linear_linear(action="update", id="ABC-123", labels=["bug", "existing-label"])
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
**Labels array replaces all labels.** Fetch current labels first, then append.
|