@sjawhar/opencode-legion-envoy 0.29.0 → 0.31.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/dist/src/server.js +20 -38
- package/package.json +1 -1
- package/skills/github/SKILL.md +4 -4
- package/skills/legion-architect/SKILL.md +41 -41
- package/skills/legion-controller/SKILL.md +44 -43
- package/skills/legion-worker/SKILL.md +29 -23
- package/skills/linear/SKILL.md +1 -1
package/dist/src/server.js
CHANGED
|
@@ -13874,8 +13874,17 @@ var legionRole = _enum2(LEGION_ROLES);
|
|
|
13874
13874
|
var requiredUnknown = unknown().refine((value) => value !== undefined, {
|
|
13875
13875
|
message: "Required"
|
|
13876
13876
|
});
|
|
13877
|
-
var
|
|
13878
|
-
|
|
13877
|
+
var LIFECYCLE_STATUSES = [
|
|
13878
|
+
"triage",
|
|
13879
|
+
"icebox",
|
|
13880
|
+
"backlog",
|
|
13881
|
+
"todo",
|
|
13882
|
+
"in_progress",
|
|
13883
|
+
"testing",
|
|
13884
|
+
"needs_review",
|
|
13885
|
+
"retro",
|
|
13886
|
+
"done"
|
|
13887
|
+
];
|
|
13879
13888
|
var architectCapability = strictObject({
|
|
13880
13889
|
tree: nonEmptyString,
|
|
13881
13890
|
sessionId: nonEmptyString,
|
|
@@ -13928,33 +13937,10 @@ var LegionDaemonApi = {
|
|
|
13928
13937
|
request: architectCapability.extend({ generation: number2().int() }),
|
|
13929
13938
|
response: object({})
|
|
13930
13939
|
},
|
|
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
13940
|
WaveRelease: {
|
|
13940
|
-
request: architectCapability.extend({
|
|
13941
|
+
request: architectCapability.extend({ issues: array(nonEmptyString).optional() }),
|
|
13941
13942
|
response: object({ released: array(nonEmptyString) })
|
|
13942
13943
|
},
|
|
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
13944
|
Escalate: {
|
|
13959
13945
|
request: architectCapability.extend({
|
|
13960
13946
|
kind: _enum2(["re-file", "capacity", "cross-tree"]),
|
|
@@ -13962,10 +13948,6 @@ var LegionDaemonApi = {
|
|
|
13962
13948
|
}),
|
|
13963
13949
|
response: object({})
|
|
13964
13950
|
},
|
|
13965
|
-
IssueClose: {
|
|
13966
|
-
request: architectCapability.extend({ issue: nonEmptyString, comment: string2().optional() }),
|
|
13967
|
-
response: object({})
|
|
13968
|
-
},
|
|
13969
13951
|
ProvisioningCredential: {
|
|
13970
13952
|
request: architectCapability.extend({ issue: nonEmptyString }),
|
|
13971
13953
|
response: object({ token: nonEmptyString })
|
|
@@ -14028,16 +14010,16 @@ var LegionDaemonApi = {
|
|
|
14028
14010
|
secret: nonEmptyString
|
|
14029
14011
|
})
|
|
14030
14012
|
},
|
|
14031
|
-
|
|
14032
|
-
request: controllerIssue
|
|
14013
|
+
IssueStatus: {
|
|
14014
|
+
request: controllerIssue.extend({
|
|
14015
|
+
status: _enum2(LIFECYCLE_STATUSES),
|
|
14016
|
+
tree: nonEmptyString.optional(),
|
|
14017
|
+
sessionId: nonEmptyString.optional()
|
|
14018
|
+
}),
|
|
14033
14019
|
response: object({})
|
|
14034
14020
|
},
|
|
14035
|
-
|
|
14036
|
-
request:
|
|
14037
|
-
response: object({ result: _enum2(["spawned", "queued"]) })
|
|
14038
|
-
},
|
|
14039
|
-
Backlog: {
|
|
14040
|
-
request: controllerIssue.extend({ marker: nonEmptyString }),
|
|
14021
|
+
GatesRegister: {
|
|
14022
|
+
request: architectCapability.extend({ issue: nonEmptyString, askId: nonEmptyString }),
|
|
14041
14023
|
response: object({})
|
|
14042
14024
|
},
|
|
14043
14025
|
Grant: {
|
package/package.json
CHANGED
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,19 @@ 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.
|
|
67
72
|
|
|
68
73
|
Write one root specification containing the accepted scope, adoption/decomposition,
|
|
69
74
|
waves, acceptance criteria, and integration test. When the config-armed root design gate
|
|
@@ -71,20 +76,18 @@ applies, run this exact sequence **before any Legion-role spawn**, including a
|
|
|
71
76
|
sub-architect:
|
|
72
77
|
|
|
73
78
|
```text
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
context: "<what the tree is, what triggered the gate>",
|
|
80
|
-
question: "<specification summary and the decision requested>"
|
|
79
|
+
dispatch_artifact({ issue: "<root issue>", name: "spec.md", content: "<root specification>", primary: true, summary: "<one-line summary>" })
|
|
80
|
+
askId = dispatch_ask({
|
|
81
|
+
issue: "<root issue>",
|
|
82
|
+
question: "<specification summary and the decision requested>",
|
|
83
|
+
options: [{ label: "Approve" }, ...]
|
|
81
84
|
})
|
|
85
|
+
legion({ op: "register_gate", issue: "<root issue>", askId })
|
|
82
86
|
```
|
|
83
87
|
|
|
84
88
|
Then park. Do not release a wave or spawn a Legion role until a later delivered wake
|
|
85
|
-
shows `
|
|
86
|
-
|
|
87
|
-
this sequence.
|
|
89
|
+
shows `design-approved` on the root. Approval covers the entire tree: later waves,
|
|
90
|
+
re-scopes, and integration-failure children do not repeat this sequence.
|
|
88
91
|
|
|
89
92
|
## 2. Children in flight
|
|
90
93
|
|
|
@@ -92,7 +95,7 @@ Release only the next useful wave, then give its owners their work. A release is
|
|
|
92
95
|
explicit lifecycle write:
|
|
93
96
|
|
|
94
97
|
```text
|
|
95
|
-
legion({ op: "
|
|
98
|
+
legion({ op: "release_wave", issues: ["LEGION-41", "LEGION-42"] })
|
|
96
99
|
```
|
|
97
100
|
|
|
98
101
|
After release, spawn each relevant owner; for example:
|
|
@@ -100,7 +103,7 @@ After release, spawn each relevant owner; for example:
|
|
|
100
103
|
```text
|
|
101
104
|
legion({
|
|
102
105
|
op: "spawn_worker",
|
|
103
|
-
issue: "
|
|
106
|
+
issue: "LEGION-41",
|
|
104
107
|
role: "architect",
|
|
105
108
|
task: "Own this child through its lifecycle and report its evidence."
|
|
106
109
|
})
|
|
@@ -122,7 +125,7 @@ and current `main` integration surface:
|
|
|
122
125
|
```text
|
|
123
126
|
legion({
|
|
124
127
|
op: "spawn_worker",
|
|
125
|
-
issue: "
|
|
128
|
+
issue: "LEGION-40",
|
|
126
129
|
role: "tester",
|
|
127
130
|
task: "Verify this parent issue against its acceptance criteria on current main; return reproducible integration evidence."
|
|
128
131
|
})
|
|
@@ -181,15 +184,12 @@ If anything changes the approved head, return to review; do not let the merger p
|
|
|
181
184
|
|
|
182
185
|
## 7. Close
|
|
183
186
|
|
|
184
|
-
After the merge result and sign-off are recorded, close this issue
|
|
185
|
-
|
|
187
|
+
After the merge result and sign-off are recorded, post the sign-off and close this issue
|
|
188
|
+
through the Legion write surface:
|
|
186
189
|
|
|
187
190
|
```text
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
issue: "owner/repo#40",
|
|
191
|
-
comment: "<sign-off: scope, integration evidence, review, retro, Sami approval, and merge>"
|
|
192
|
-
})
|
|
191
|
+
dispatch_comment({ issue: "LEGION-40", body: "<sign-off: scope, integration evidence, review, retro, Sami approval, and merge>" })
|
|
192
|
+
legion({ op: "set_status", issue: "LEGION-40", status: "done" })
|
|
193
193
|
```
|
|
194
194
|
|
|
195
195
|
Closing a child supplies the closure event to its parent. Do not close a parent until the
|
|
@@ -220,6 +220,6 @@ corresponding lifecycle procedure.
|
|
|
220
220
|
|
|
221
221
|
Controller-actionable matters are exactly re-filing a genuinely independent child,
|
|
222
222
|
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 `
|
|
223
|
+
everything else in the tree, or use `dispatch_ask` for a human question; workers may reach
|
|
224
|
+
Sami directly with `dispatch_ask` the same way. Do not create a wait loop for any wake
|
|
225
225
|
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
|
|
@@ -133,11 +135,13 @@ capability it needs; invoke GitHub through the credential helper:
|
|
|
133
135
|
legion gh -- <gh args…>
|
|
134
136
|
```
|
|
135
137
|
|
|
136
|
-
## GitHub comment attribution
|
|
138
|
+
## GitHub PR comment attribution
|
|
137
139
|
|
|
138
|
-
Append this exact structured footer to **every**
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
Append this exact structured footer to **every** pull-request comment and review that this
|
|
141
|
+
phase posts on GitHub. It preserves session provenance on the artifact itself so work stays
|
|
142
|
+
attributable to the session that produced it. Dispatch comments carry session provenance
|
|
143
|
+
natively through their own `actor`/`origin` fields; this footer is only for GitHub PR
|
|
144
|
+
artifacts:
|
|
141
145
|
|
|
142
146
|
```html
|
|
143
147
|
<!-- legion: {"session":"<session-id>","phase":"<phase>"} -->
|
|
@@ -146,7 +150,7 @@ itself so work stays attributable to the session that produced it:
|
|
|
146
150
|
For example:
|
|
147
151
|
|
|
148
152
|
```bash
|
|
149
|
-
legion gh --
|
|
153
|
+
legion gh -- pr comment <pr-number> \
|
|
150
154
|
--body $'Verification complete.\n\n<!-- legion: {"session":"<session-id>","phase":"<phase>"} -->' \
|
|
151
155
|
--repo <owner>/<repo>
|
|
152
156
|
```
|
|
@@ -159,17 +163,19 @@ procedure:
|
|
|
159
163
|
|
|
160
164
|
```bash
|
|
161
165
|
cd -- "$LEGION_WORKSPACE" && \
|
|
162
|
-
jj -R "$LEGION_WORKSPACE" bookmark set legion
|
|
163
|
-
jj -R "$LEGION_WORKSPACE" git push --bookmark legion
|
|
166
|
+
jj -R "$LEGION_WORKSPACE" bookmark set legion/<KEY> && \
|
|
167
|
+
jj -R "$LEGION_WORKSPACE" git push --bookmark legion/<KEY>
|
|
164
168
|
```
|
|
165
169
|
|
|
166
170
|
The provisioned issue workspace configures `credential.helper` with the daemon's absolute
|
|
167
171
|
credential command, so `jj -R "$LEGION_WORKSPACE" git push` authenticates transparently
|
|
168
172
|
through the same session capability. Never handle a token.
|
|
169
173
|
|
|
170
|
-
Then open the pull request with `legion gh -- pr create`. The
|
|
171
|
-
|
|
172
|
-
|
|
174
|
+
Then open the pull request with `legion gh -- pr create`. The PR body **must** contain the
|
|
175
|
+
line `Dispatch: <KEY>` — the daemon's fallback link from a PR to its Dispatch issue when the
|
|
176
|
+
branch name alone is ambiguous. The credential helper and `legion gh` provide the GitHub
|
|
177
|
+
identity; never export, fetch, or replace a token. Other phases advance the existing branch
|
|
178
|
+
rather than creating a replacement bookmark or PR.
|
|
173
179
|
|
|
174
180
|
## PR body and merge-queue discipline
|
|
175
181
|
|
|
@@ -243,13 +249,13 @@ cd -- "$LEGION_WORKSPACE" && \
|
|
|
243
249
|
```
|
|
244
250
|
|
|
245
251
|
If the issue bookmark exists locally, advance it and push it with the provisioned
|
|
246
|
-
credential helper. `--
|
|
247
|
-
first push:
|
|
252
|
+
credential helper. `--bookmark` also publishes the locally provisioned bookmark on its
|
|
253
|
+
first push — a bookmark not yet tracking a remote one is tracked automatically:
|
|
248
254
|
|
|
249
255
|
```bash
|
|
250
256
|
cd -- "$LEGION_WORKSPACE" && \
|
|
251
|
-
jj -R "$LEGION_WORKSPACE" bookmark set legion
|
|
252
|
-
jj -R "$LEGION_WORKSPACE" git push --bookmark legion
|
|
257
|
+
jj -R "$LEGION_WORKSPACE" bookmark set legion/<KEY> && \
|
|
258
|
+
jj -R "$LEGION_WORKSPACE" git push --bookmark legion/<KEY>
|
|
253
259
|
```
|
|
254
260
|
|
|
255
261
|
Do not report phase completion until the write, existence check, and handoff commit
|
|
@@ -279,5 +285,5 @@ session, if this phase's work needs to run again.
|
|
|
279
285
|
|
|
280
286
|
When blocked on lifecycle, scope, or cross-phase matters, `envoy_publish` the owning
|
|
281
287
|
architect a concise message: issue, phase, verified observation, what you tried, and the
|
|
282
|
-
decision required. Reach for `
|
|
288
|
+
decision required. Reach for `dispatch_ask` yourself only for a standalone human question
|
|
283
289
|
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.
|