@sjawhar/claude-envoy 1.11.3 → 1.12.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/.claude-plugin/plugin.json +1 -1
- package/.omp-plugin/plugin.json +1 -1
- package/dist/dispatch.js +1 -0
- package/dist/envoy-channel.js +16 -4
- package/dist/session-hook.js +1 -0
- package/legion/skills/legion-architect/SKILL.md +3 -3
- package/legion/skills/legion-architect/references/design-gate.md +82 -35
- package/legion/skills/legion-architect/references/wake-routing.md +4 -4
- package/legion/skills/legion-controller/SKILL.md +1 -1
- package/legion/skills/legion-controller/references/parked-decisions.md +7 -1
- package/legion/skills/legion-controller/references/triage.md +3 -3
- package/legion/skills/legion-controller/references/wakes.md +6 -4
- package/legion/skills/legion-worker/references/merge-gate.md +7 -3
- package/package.json +2 -2
- package/skills/outside-message/SKILL.md +4 -2
package/.omp-plugin/plugin.json
CHANGED
package/dist/dispatch.js
CHANGED
|
@@ -16849,6 +16849,7 @@ var LEGION_ROLES = [
|
|
|
16849
16849
|
"reviewer",
|
|
16850
16850
|
"merger"
|
|
16851
16851
|
];
|
|
16852
|
+
var GATED_STEPS = ["decisions", "criteria", "spec"];
|
|
16852
16853
|
var PROJECT_TOKEN = /^[a-z0-9]+$/;
|
|
16853
16854
|
var DISPATCH_KEY_PATTERN = /^([A-Z][A-Z0-9]*)-([0-9]+)$/;
|
|
16854
16855
|
function roleForPhase(phase) {
|
package/dist/envoy-channel.js
CHANGED
|
@@ -38498,6 +38498,7 @@ var LEGION_ROLES = [
|
|
|
38498
38498
|
"reviewer",
|
|
38499
38499
|
"merger"
|
|
38500
38500
|
];
|
|
38501
|
+
var GATED_STEPS = ["decisions", "criteria", "spec"];
|
|
38501
38502
|
var PROJECT_TOKEN = /^[a-z0-9]+$/;
|
|
38502
38503
|
var DISPATCH_KEY_PATTERN = /^([A-Z][A-Z0-9]*)-([0-9]+)$/;
|
|
38503
38504
|
function roleForPhase(phase) {
|
|
@@ -52032,6 +52033,15 @@ var legionGateView = object({
|
|
|
52032
52033
|
currentVersion: number2().int().positive(),
|
|
52033
52034
|
approvedVersion: number2().int().positive().nullable()
|
|
52034
52035
|
});
|
|
52036
|
+
var legionDesignStep = _enum2(GATED_STEPS);
|
|
52037
|
+
var legionGatedStepView = object({
|
|
52038
|
+
step: legionDesignStep,
|
|
52039
|
+
artifactId: nonEmptyString.optional(),
|
|
52040
|
+
currentVersion: number2().int().positive().optional(),
|
|
52041
|
+
approvedVersion: number2().int().positive().nullable(),
|
|
52042
|
+
status: _enum2(["unregistered", "waiting", "approved", "changes_requested"]),
|
|
52043
|
+
changesReason: nonEmptyString.optional()
|
|
52044
|
+
});
|
|
52035
52045
|
var legionSlotView = object({
|
|
52036
52046
|
index: number2().int().nonnegative(),
|
|
52037
52047
|
admittedAt: timestamp,
|
|
@@ -52119,6 +52129,7 @@ var legionIssue = object({
|
|
|
52119
52129
|
workers: partialRecord(string2(), legionPhaseView),
|
|
52120
52130
|
pullRequest: legionPullRequestView.optional(),
|
|
52121
52131
|
designGate: legionGateView.optional(),
|
|
52132
|
+
gatedSteps: array(legionGatedStepView).optional(),
|
|
52122
52133
|
slot: legionSlotView.optional(),
|
|
52123
52134
|
waitingOn: legionWaitingOn.optional(),
|
|
52124
52135
|
check: legionCheckView.optional(),
|
|
@@ -52301,7 +52312,7 @@ var LegionControllerRegisterResponse = object({
|
|
|
52301
52312
|
});
|
|
52302
52313
|
var LegionControllerSecretResponse = object({
|
|
52303
52314
|
secret: nonEmptyString,
|
|
52304
|
-
|
|
52315
|
+
designGateSteps: array(legionDesignStep)
|
|
52305
52316
|
});
|
|
52306
52317
|
var LegionControllerCredentialResponse = object({
|
|
52307
52318
|
owners: record(nonEmptyString, object({
|
|
@@ -52405,7 +52416,8 @@ var LegionGateRegisterRequest = strictObject({
|
|
|
52405
52416
|
grantId: nonEmptyString,
|
|
52406
52417
|
issue: nonEmptyString,
|
|
52407
52418
|
artifactId: uuid2(),
|
|
52408
|
-
version: number2().int().positive()
|
|
52419
|
+
version: number2().int().positive(),
|
|
52420
|
+
step: legionDesignStep.optional()
|
|
52409
52421
|
});
|
|
52410
52422
|
var LegionWaveReleaseRequest = strictObject({
|
|
52411
52423
|
grantId: nonEmptyString,
|
|
@@ -55133,9 +55145,9 @@ var import_nats2 = __toESM(require_mod4(), 1);
|
|
|
55133
55145
|
|
|
55134
55146
|
// package.json
|
|
55135
55147
|
var name = "@sjawhar/claude-envoy";
|
|
55136
|
-
var version2 = "1.
|
|
55148
|
+
var version2 = "1.12.0";
|
|
55137
55149
|
var legion = {
|
|
55138
|
-
daemonApiVersion:
|
|
55150
|
+
daemonApiVersion: 55
|
|
55139
55151
|
};
|
|
55140
55152
|
|
|
55141
55153
|
// src/channel-forwarder.ts
|
package/dist/session-hook.js
CHANGED
|
@@ -15848,6 +15848,7 @@ var LEGION_ROLES = [
|
|
|
15848
15848
|
"reviewer",
|
|
15849
15849
|
"merger"
|
|
15850
15850
|
];
|
|
15851
|
+
var GATED_STEPS = ["decisions", "criteria", "spec"];
|
|
15851
15852
|
var PROJECT_TOKEN = /^[a-z0-9]+$/;
|
|
15852
15853
|
var DISPATCH_KEY_PATTERN = /^([A-Z][A-Z0-9]*)-([0-9]+)$/;
|
|
15853
15854
|
function roleForPhase(phase) {
|
|
@@ -29,9 +29,9 @@ You own one issue tree from its first decision to the merge that closes it. The
|
|
|
29
29
|
- A child issue is a separately deliverable change: its own pull request in its own repository, or its own deploy. Never use a child to track a to-do, a fix found in review, a proof of the root's acceptance, or a part of your own work: do it in this tree. Each child costs a workspace, an admission wait and a review loop. Before any `dispatch issue` that creates a child, and before `release_children`, run the red-team check in `skill://legion-architect/references/decomposition.md`.
|
|
30
30
|
- Every child names the running system its criteria are proven on (the command, service or page the tester drives) and the repository skill that drives it. If the repository cannot exercise a criterion end to end, building that path is work of this tree.
|
|
31
31
|
- The issue's primary document is the root spec: extend it in place, because a second "spec" artifact replaces the human's document. Mark each requirement of it as a `requirement` block, and write the issue's Verification document (`verification.md`) beside it: one `check` block per thing that must be proved, each with `serves` naming the requirements it serves, its `surface` (`browser`, `cli` or `api`), its `run`, its `when` (`before_merge`, `after_merge` or `default_branch`) and its `negative_control`. The checks are the gate every later role reads: the implementer and the tester record a result against each, and the reviewer approves only once every `before_merge` check of the issue has a passing result current at the head. The waves and the plan's tasks go in the children and the planner's `plan` handoff, not the root spec.
|
|
32
|
-
- Read `skill://legion-architect/references/design-gate.md` before you write the root spec and before any edit to it after approval, because any new version closes the gate. It also
|
|
33
|
-
- File every child of your decomposition before `dispatch request-approval`, and before `register_gate`
|
|
34
|
-
- A root architect then ends its turn and releases no wave until a delivered wake shows `design-approved
|
|
32
|
+
- Read `skill://legion-architect/references/design-gate.md` before you write the root spec and before any edit to it after approval, because any new version closes the gate. It also names the design gate steps your `Legion addressing` line declares, the document each step records, and says that a sub-architect runs none.
|
|
33
|
+
- File every child of your decomposition before `dispatch request-approval`, and before `register_gate` where your line gates no step: the gate's opening starts your root's own first phase unless Dispatch shows an open child under it, so a child filed after it opens leaves the root working beside its children.
|
|
34
|
+
- A root architect then ends its turn and releases no wave until every gated step is open: a delivered wake shows `design-approved` on the root, and under more than one step `gatedSteps` in your record (the `legion` tool's `read_record`) shows each step `approved`. The daemon starts no phase in the tree before that. A root that waited on a person's answer gets no `design-approved` notice: a resume task saying "this resume is your design-approved wake" is that wake. A sub-architect waits for none and releases its children with `release_children`.
|
|
35
35
|
|
|
36
36
|
## 2. Children in flight
|
|
37
37
|
|
|
@@ -30,21 +30,35 @@ answer, Slack message or direct message of a `Filed for <person> at their reques
|
|
|
30
30
|
evidence of a controller's `Filed for Legion from <role> on <issue>:` line
|
|
31
31
|
([The `Filed for` line](skill://legion-controller/references/finding-asks.md#the-filed-for-line)).
|
|
32
32
|
|
|
33
|
-
## Which
|
|
33
|
+
## Which steps are gated
|
|
34
34
|
|
|
35
|
-
The
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
The `Design gate steps` sentence of your `Legion addressing` line names the steps this deployment
|
|
36
|
+
gates, in the order it gates them, each in backticks: ``Design gate steps: `spec`.``,
|
|
37
|
+
``Design gate steps: `decisions`, `criteria`.``, or `Design gate steps: none.` A sub-architect on
|
|
38
|
+
a child issue has no such sentence and never runs the gate.
|
|
39
|
+
|
|
40
|
+
Each step is one document of the root issue, and the daemon keeps one gate record for each step:
|
|
41
|
+
|
|
42
|
+
| Step | The document it records |
|
|
43
|
+
| --- | --- |
|
|
44
|
+
| `decisions` | the issue's decisions document (`decisions.md`) |
|
|
45
|
+
| `criteria` | its Verification document (`verification.md`) |
|
|
46
|
+
| `spec` | its primary document |
|
|
47
|
+
|
|
48
|
+
The tree's work starts when every step the sentence names is open, so run the armed sequence below
|
|
49
|
+
once for each of them, each on that step's own document. Under `Design gate steps: none.` write
|
|
50
|
+
the spec and register it with `register_gate` at its current version, with no `step` and no
|
|
51
|
+
approval step: do not request approval and do not wait for `design-approved`; the gate opens at
|
|
52
|
+
registration. File every child of your decomposition before that registration, since the gate's
|
|
53
|
+
opening starts the root's own first phase unless Dispatch shows an open child under it.
|
|
42
54
|
|
|
43
55
|
## The armed gate
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
your decomposition filed before step 4, since the gate's opening starts the root's own first
|
|
47
|
-
unless Dispatch shows an open child under it
|
|
57
|
+
Run this exact sequence for each gated step, before the tree's work starts, with every child of
|
|
58
|
+
your decomposition filed before step 4, since the gate's opening starts the root's own first
|
|
59
|
+
phase unless Dispatch shows an open child under it. One approval request covers the `criteria`
|
|
60
|
+
and the `spec` step together (step 4), and each of them is registered on its own document
|
|
61
|
+
(step 5); the `decisions` step is a request of its own.
|
|
48
62
|
|
|
49
63
|
1. Extend the primary document in place:
|
|
50
64
|
|
|
@@ -74,22 +88,31 @@ unless Dispatch shows an open child under it:
|
|
|
74
88
|
```
|
|
75
89
|
|
|
76
90
|
`--include-verification` asks for the spec and the Verification document in one request, each
|
|
77
|
-
at its own current settled version, and the human's answer records a review on each
|
|
78
|
-
|
|
79
|
-
document has no settled
|
|
91
|
+
at its own current settled version, and the human's answer records a review on each: that one
|
|
92
|
+
request covers the `criteria` and the `spec` step together. An issue with no `verification.md`
|
|
93
|
+
is refused `NO_VERIFICATION_DOCUMENT`, and one whose Verification document has no settled
|
|
94
|
+
version `NO_VERSION`.
|
|
95
|
+
|
|
96
|
+
The `decisions` step is a request of its own, since `--include-verification` is for the issue's
|
|
97
|
+
spec alone and any other document is refused `NOT_SPEC_DOCUMENT`:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
dispatch request-approval --issue <root issue> --artifact decisions.md --summary '<what the human is approving> Reviews: <gap result>, <claim result>; <what they do not show>.'
|
|
101
|
+
```
|
|
80
102
|
|
|
81
103
|
`--summary` says in one to three sentences what the human is approving and nothing else (no
|
|
82
104
|
commentary and no open question), then one sentence with both reviews' results and what they do
|
|
83
105
|
not show, for example `Reviews: no open gap, 14 of 14 claims verified; neither shows that the
|
|
84
106
|
design works once built.` The primary document is the default; the
|
|
85
107
|
command prints `Approval requested for spec.md (document id <UUID>) at version <N> (ask <id>)`.
|
|
86
|
-
5. Register
|
|
108
|
+
5. Register each gated step's document and version with the daemon, one call for each step:
|
|
87
109
|
|
|
88
110
|
```ts
|
|
89
111
|
legion({
|
|
90
112
|
op: "register_gate",
|
|
91
113
|
issue: "<root issue>",
|
|
92
|
-
|
|
114
|
+
step: "<decisions|criteria|spec>", // the step this document is for
|
|
115
|
+
artifactId: "<UUID>", // that step's document id, which the request printed
|
|
93
116
|
version: <N>, // the version the human is asked to approve
|
|
94
117
|
})
|
|
95
118
|
```
|
|
@@ -99,10 +122,12 @@ unless Dispatch shows an open child under it:
|
|
|
99
122
|
document's approval events by that id alone; the `legion` tool looks a slug or file name up in
|
|
100
123
|
Dispatch and hands the daemon the id.
|
|
101
124
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
125
|
+
Each step records its own document, so register the spec's id and version under `step: "spec"`
|
|
126
|
+
and the Verification document's under `step: "criteria"`: the request that named both sent an
|
|
127
|
+
`artifact.approved` event for each, and each step opens on its own document's alone. Leave
|
|
128
|
+
`step` out only where your line names one step, which is the whole list a one-step deployment
|
|
129
|
+
gates. Register no step your line does not name: a step the deployment does not gate is
|
|
130
|
+
refused, and a record of it would hold nothing open.
|
|
106
131
|
|
|
107
132
|
Facts about the request:
|
|
108
133
|
|
|
@@ -124,18 +149,38 @@ Facts about the request:
|
|
|
124
149
|
latest version Dispatch holds for that document (the message names both), and a version Dispatch
|
|
125
150
|
never wrote is no registration: the gate is as it was. Read the document's latest version, check
|
|
126
151
|
it is the spec the human is asked to approve, and `register_gate` that version.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
152
|
+
- `register_gate` refused 400 `GATE_STEP_REQUIRED` means this deployment gates more than one step
|
|
153
|
+
and your call named none; the message names the steps it gates. Call again with `step` naming
|
|
154
|
+
the one that document is for.
|
|
155
|
+
- `register_gate` refused 409 `GATE_STEP_NOT_CONFIGURED` means the `step` you named is not one
|
|
156
|
+
this deployment gates; the message names the steps it does gate. Register those and no other.
|
|
157
|
+
- `register_gate` refused 409 `GATE_STEP_DOCUMENT_MISMATCH` means the document you named is not
|
|
158
|
+
that step's document of that issue; the message names the step and the document it records.
|
|
159
|
+
Register that step's own document, as the table in "Which steps are gated" says.
|
|
160
|
+
|
|
161
|
+
Each of those four refusals is given before anything is recorded, so the gate is as it was.
|
|
162
|
+
|
|
163
|
+
A `design-approved` or `design-changes-requested` notice carries a version and a reason and names
|
|
164
|
+
no step. Read your own issue record with the `legion` tool's `read_record` to see which step the
|
|
165
|
+
review was of: `gatedSteps` carries one entry for each gated step, with its `artifactId`, its
|
|
166
|
+
`currentVersion`, its `approvedVersion`, its `status` (`unregistered`, `waiting`, `approved` or
|
|
167
|
+
`changes_requested`) and the `changesReason` of a step whose reviewer asked for changes. That
|
|
168
|
+
list, and not the notice, says which steps are open and which still hold the tree's work.
|
|
169
|
+
|
|
170
|
+
Then end your turn. The registrations hand those documents to the human: when nothing else of the
|
|
171
|
+
tree is at work, the root returns its admission slot and waits on its design gate
|
|
172
|
+
(`awaiting_person`), as a root question handed to the human does ("Every turn" in
|
|
173
|
+
`skill://legion-architect/SKILL.md`). Stay live to read the human's comments and replies. Their
|
|
174
|
+
approval or changes request delivers your next task, and so does a newer spec version once
|
|
175
|
+
Dispatch shows the request back on your turn. While the root waits on a question instead, the
|
|
176
|
+
human's approval or changes request of the spec answers that wait too, and the task says how the
|
|
177
|
+
spec stands. Release no wave until every gated step is open: a delivered wake shows
|
|
178
|
+
`design-approved` on the root, or a task says its design gate is open, and under more than one
|
|
179
|
+
step `gatedSteps` in your record shows each one `approved`. The daemon sends a root waiting on a
|
|
180
|
+
person no `design-approved` notice, and "this resume is your design-approved wake" is that wake.
|
|
181
|
+
On `design-changes-requested`: revise the document that step records as the human's reason asks,
|
|
182
|
+
run step 3's two reviews on the new version, resolve every finding, request approval again and
|
|
183
|
+
end your turn. A
|
|
139
184
|
`design-changes-requested` may name a version below the latest, or arrive after another for the
|
|
140
185
|
same version: it is a request the human made that reached the daemon late, and no approval came
|
|
141
186
|
after it. Each request arrives once. The gate is as the newest review left it; still read the
|
|
@@ -143,9 +188,11 @@ reason, and answer it in the spec if the latest version does not already.
|
|
|
143
188
|
|
|
144
189
|
## After approval
|
|
145
190
|
|
|
146
|
-
|
|
147
|
-
`design-approved`-type wake says so; you know from
|
|
148
|
-
|
|
191
|
+
Each step's approval is pinned to the version of the document that step records: a new version of
|
|
192
|
+
that document closes that step again, and no `design-approved`-type wake says so; you know from
|
|
193
|
+
your own edit, or from the `artifact.version` event on your issue. One step closing holds the
|
|
194
|
+
tree's work whatever the other steps stand at. The exceptions are the version a human's own
|
|
195
|
+
answer to a decision block
|
|
149
196
|
writes when that answer is the whole change since the version they approved, and a version whose
|
|
150
197
|
text is exactly the text of the version they approved, with no decision block in it decided
|
|
151
198
|
since, such as an accidental edit undone exactly: for either, Dispatch records an `approved`
|
|
@@ -18,14 +18,14 @@ and 7 (a conflict, the hand-over, the close) is in
|
|
|
18
18
|
|
|
19
19
|
| Wake | Procedure |
|
|
20
20
|
| --- | --- |
|
|
21
|
-
| `carried-gate-opened` | Only a root whose catch-up said `carried` gets it. Its `version` and `reason`: a human approved the old root's spec document at `version`, which opened the gate this root carried; the daemon has released any READY it refused and started any child waiting on it. It is not `design-approved`: do not proceed to section 2 or release a wave because of it, and start nothing. |
|
|
21
|
+
| `carried-gate-opened` | Only a root whose catch-up said `carried` gets it. Its `version` and `reason`: a human approved the old root's spec document at `version`, which opened the gate this root carried; the daemon has released any READY it refused and started any child waiting on it. It names no step: the carry brings the old root's gate records, and the catch-up reports every gated step the carry did not bring as unregistered, so register each of those on this issue's own document for that step (`skill://legion-architect/references/design-gate.md`). It is not `design-approved`: do not proceed to section 2 or release a wave because of it, and start nothing. |
|
|
22
22
|
| `child-status` | A child of your tree left the workflow or re-entered it, or joined your tree from the admission queue; the reason names the child and its new status, or reads `<KEY> joined the tree of <ROOT> from the admission queue`. `todo` (a human's move, your `release_children`, or your `rerun_child`) means it runs again under your tree from planning, and the daemon starts it; you start nothing. A child held for a blocker starts the same way once nothing holds it, and the reason, naming it and its root, says what cleared the hold: its last blocker's close, naming the issues it waited on, now done, or a person or you taking its blocked-by links off; you start nothing for it either. `backlog`, `icebox` or `triage` (a human's move, or your `park_child`) means the daemon has suspended its workers, and it advances no further until it is set back to `todo`. Joined from the admission queue means a root that waited for a slot and never ran, which a person made a child of your tree or your `release_children` adopted, now runs in your tree with no architect of its own and takes an admission slot of its own when its work starts, from planning when it is `todo` and parked otherwise; one that waits on an unfinished blocked-by issue is held for that blocker like any other child, and starts when nothing holds it; you start nothing. A reason saying an issue is beneath an ancestor whose reviewed head the daemon has already accepted is a person's `todo` the daemon recorded and holds at `admitted`: nothing starts it until that ancestor closes, and a `request_backward_move` of the ancestor is what runs it under the ancestor now. What the rest of the tree does is your decision. When a park leaves no child that an issue in `accepting` waits on, run that issue's last round (`skill://legion-architect/references/acceptance.md`). |
|
|
23
23
|
| `child-closed` | Read the child completion and remaining open children. Re-scope open work, park obsolete work for good with `park_child` (first take its link off each sibling that waits on it, or park that sibling too, as section 2 says), and file any new child the result calls for, after the red-team check in `skill://legion-architect/references/decomposition.md`, and release it at once with `release_children`, blocked by what it builds on (section 2). When a worker waits on this child for a missing surface (see `phase-finished`), tell it to continue with `envoy_publish` to its role topic. The last child's close is not the end-game (section 3). When it was the last child an issue in `accepting` waits on, run that issue's last round (`skill://legion-architect/references/acceptance.md`). |
|
|
24
24
|
| `child-release-stalled` | Its `reason` names a child your `release_children` released that your tree has not recorded, its parent, and why, one of three: the parent's release has not reached the daemon after three tries; or GitHub did not answer the App installation read of a repository on the child's parent chain, and the wave left the child unrecorded because a parent on that chain is not `todo`, or did not write the child `todo` because its parent is not recorded; or the daemon's recovery of such a repository, once GitHub answered, still left children unrecorded (one notice per tree lists them all, each with its parent). Read the parent's record with the `legion` tool's `read_record` (`issue` the parent); release the parent first when the `reason` says to, then run `release_children` for the child again once the parent is recorded in your tree, and tell the controller if it never is. |
|
|
25
25
|
| `child-not-recorded` | Its `reason` names a child your tree did not record and why: its repository resolves to none (neither it nor an ancestor names one, and the project has no default), or to one not mapped to the project, or to one the daemon leaves out for a missing GitHub App. The daemon said why on the child too. Set the child's repository to one of the project's (`dispatch issue-update --repo owner/name`), then release it again with `release_children`; tell the controller when the repository it needs is not served. |
|
|
26
26
|
| `children-first` | An issue of your tree starts no phase of its own while Dispatch shows an open child under it, a child you filed and have not released included. The reason names the parent and each open child with the Dispatch status that holds it, and each cycle it found (a child `blocked_by` an issue above it on its own parent chain, which only a person taking that link off ends: open a `dispatch ask`). It comes once per issue and run of its tree. Release every child that should run, and close, `park_child` or re-parent the rest; the daemon starts the parent's first phase at its next admission slot once the last open child has left the workflow. You start nothing. |
|
|
27
|
-
| `design-approved` | Its `version` is the approved one. A human approved the
|
|
28
|
-
| `design-changes-requested` | Its `version` and `reason`. A human asked for changes to
|
|
27
|
+
| `design-approved` | Its `version` is the approved one, and it names no step: under more than one gated step, read `gatedSteps` in your own record (the `legion` tool's `read_record`) to see which step that version opened and whether another still holds the tree. A human approved the document of one gated step at its current version; that step is open. Proceed to section 2 once every gated step is `approved` — except on a root whose catch-up said `handed_over`: its workers already run their phases, so start nothing; the approval releases any READY the gate refused. One whose `version` is the version a human's own answer to a decision block wrote, after an earlier approval (Dispatch carried that approval to it, "After approval" in `skill://legion-architect/references/design-gate.md`), opens nothing new: carry on, and release no wave because of it. A root that was waiting on a question in `awaiting_person` gets no such notice: the task that resumes it says "Your spec is approved at version N and the design gate is open: this resume is your design-approved wake", and that resume is this wake — proceed to section 2 on it. |
|
|
28
|
+
| `design-changes-requested` | Its `version` and `reason`, and no step: under more than one gated step, read `gatedSteps` in your own record (the `legion` tool's `read_record`) and take the step whose `status` is `changes_requested`. A human asked for changes to that step's document at `version`, for `reason`. Revise that document as the reason asks and request approval again as "The armed gate" in `skill://legion-architect/references/design-gate.md` says; end your turn; that step is closed. On a root that was waiting on a question in `awaiting_person`, a review the human gave after the wait began comes in the resume task, not in this notice, whether it answered the wait or came after something else did: the task names the review with its version and reason, unless a later approval superseded it. A `version` below the latest, or a second request for one version, is a request that reached the daemon late: read it as the same section's last paragraph says. Each request arrives once. |
|
|
29
29
|
| `phase-finished` | The daemon has already moved the issue to its next phase by its fixed table and started that phase's role; you start nothing. Read the finishing phase's handoff on the Dispatch issue (`dispatch handoff-read --issue <issue> --phase <word>`); if it shows unresolved gaps, tell the role now working the issue (`envoy_publish` to its role topic). A `planner` notice that names a departure from the spec's design defers to the full condition in "After approval" of `skill://legion-architect/references/design-gate.md`: when the approved Summary, the requirements, the checks, the scope and the settled decisions still hold, the plan is the record; otherwise change the root spec and request approval again as that section says. A `reviewer` notice whose GitHub review is `CHANGES_REQUESTED` needs nothing from you: the daemon has returned the issue to `implementing` (Dispatch `in_progress`) and started the **implementer**, whose correction goes through the tester and the reviewer again, never straight to retro. A `reviewer` notice with an `APPROVED` review means the issue is in `accepting` and the acceptance is yours (section 6): read the evidence, then `sign_off` (a parent only after its last round, "The last round" in `skill://legion-architect/references/acceptance.md`) or send it back with `request_backward_move`. A `tester` notice with `verdict: "fail"` — its handoff carries `implementerProof.verdict: "rejected"`, or a failure naming a check with no current passing result — has gone back to the **implementer** by the daemon's table; never record that check's result from another role. A worker that reports no surface reaches the changed path sends that report instead of completing its phase, so the daemon starts nothing more on that issue and the worker stays idle in its session, not suspended: build the surface in this tree: a child issue (infrastructure, tooling, or a skill) after the red-team check in `skill://legion-architect/references/decomposition.md`, or, when the red team folds it, tell the waiting worker to move the issue back to `implementing` with `request_backward_move`, naming the surface. When it is a child, tell the waiting worker to continue with `envoy_publish` to its role topic once that child's `child-closed` arrives. That report is never a reason to advance the phase. |
|
|
30
30
|
| `pr-blocked` | Its `reason` names the pull request and the `max_fix_attempts` it reached, or reads `Issue reached review_round_cap=<N>.` (the end of this row). The count is of heads pushed onto a red verdict: no handoff is a commit, so every such push counts except a push by the review App (a planner's, tester's, reviewer's or architect's), and the head pushed onto a red the tester's red tests earned (a planned red) does not count either. Published once per exhausted count, not on every later red verdict for that count. Read the failed CI evidence and recovery attempts. The notice moves nothing, and no later CI red within the attempt that ran the count out (the counted head, and every head pushed on top of it until a push lands on a head CI judged green or on a settled head whose base branch requires no check and no workflow, the tester's red tests onto the held red included) sends the work back from `testing`, `reviewing`, `accepting`, `retro` or `merging` either: the issue stays in its phase until you move it, or a worker's own decision does (a tester's `fail`, a reviewer's request for changes). A head pushed onto green CI, or onto a settled head whose base branch requires no check and no workflow, leaves that attempt, so its red still comes as a `checks-red`. In `implementing`, give the implementer the failing checks (`envoy_publish` to its role topic). In any later phase a worker holds, tell that worker (`envoy_publish` to its role topic) to move the issue back to `implementing` with `request_backward_move`, naming the failing checks, and the daemon starts the implementer. In `accepting`, where no worker holds a phase, the move is yours: `request_backward_move` with `issue`, naming the failing checks. In `awaiting_merge`, where no worker holds a phase and a backward move is refused, open a `dispatch ask` naming the failing checks for the human who merges. Do not treat the blocked PR as final. A `review_round_cap` reason is the implementer's round count instead: every move back to an earlier phase counts one, and the notice comes again with each round past the cap. It moves nothing either, since the round that reached the cap has already started its worker. Tell the rounds apart by what announced each: a tester's fail by its `phase-finished` with `verdict: "fail"`; a request for changes by the reviewer's `phase-finished` over a `CHANGES_REQUESTED` review; the daemon's own move by a `checks-red` notice, whose reason names a conflict (`the head conflicts with <base>`), the required checks that failed (`CI is red at <head>`), or the merge queue's failed merge and its failing run (`the merge queue took pull request #<n> out at READY's head …`); a worker's `request_backward_move` by a `phase-finished` from the role that asked, whose reason reaches only the task of the worker the move started, so ask that worker; your own send-back from `accepting` by the reason you gave. Judge the cap by the rounds that are not conflicts. Conflict returns need nothing from you beyond the next round. Tester fails, requests for changes and CI-red returns (a `checks-red` notice is raised for CI only when a required check fails on code the branch pushed, or when the merge queue fails the merge) are the rounds the cap is for. Each `review_round_cap` notice is a decision you make in the turn it wakes, never one left for the next round to make: read what every counted round found, choose one of the three below, act on it in the same turn, and post it as a `dispatch comment` on the issue naming the rounds you read and the choice. **Narrow**: the change itself is sound but each round finds more around it; tell the implementer and the reviewer (`envoy_publish` to each one's role topic) which checks of the issue the next round answers; a correctness finding is still fixed in this pull request, and so is a wording nit or any clear fix in a file it touches, so the `Fast-follow:` line carries only cleanup in files this pull request does not touch. **Split**: part of the work cannot settle in this pull request and is a separately deliverable change (`skill://legion-architect`, section 1). On a child, run the red-team check in `skill://legion-architect/references/decomposition.md`, then file that part as a child of this issue's parent, never of this one (this issue closes at its own merge, and the split part usually builds on this pull request's unmerged code): `dispatch issue --project <project> --parent <parent> --title '<outcome>' --blocked-by <this issue> --spec-file -`. Release it now with `release_children`: Legion holds it on that `--blocked-by` link until this issue closes, and starts it then. On the root, narrow it to what settles here and open a `dispatch ask` for the rest (`skill://legion-worker/references/findings.md`): a person decides whether that becomes an issue. Tell the implementer and the reviewer that this pull request no longer carries it. **Accept the rounds**: each round found less than the one before it, and the last found no defect in the change itself (a stale line, a comment that states an old default); let the loop run and say why in the comment. A later notice is a new decision: accept again only when its round found less than the round you last accepted, and otherwise narrow or split. A finding that changes what the spec requires or what a check proves is a decision block in the root spec ("After approval" in `skill://legion-architect/references/design-gate.md`), and `dispatch ask` is only for a decision that needs a human's authority; neither replaces the choice. Judge a worker's backward move by the reason that worker gives. A move you ordered for a conflict found before `awaiting_merge` ("A conflicting head" in `skill://legion-architect/references/acceptance.md`) is a conflict return. The merger's move back to `reviewing` for a READY refusal (`READY_UNREVIEWED_CHANGE`, or `READY_NOT_APPROVED` when its task names no approved head), and the reviewer's no-fix request for changes that follows it so the tester runs the head, are not the work going in circles either. A move that sends back code found wrong counts with the tester fails. |
|
|
31
31
|
| `pr-merged` | The pull request merged and the issue did not close; its `reason` says why. Merged before the issue reached `awaiting_merge`: the workflow runs on, and the merge closes the issue the moment it gets there, your acceptance and retro first. Merged while the rest of the tree still runs: the reason names the pull request and every issue the close waits for, and the root waits in `awaiting_merge`, its workers kept and live and no admission slot held, until the last of them is done or parked ("The close" in `skill://legion-architect/references/acceptance.md`). Either way you start nothing and no further notice follows. |
|
|
@@ -39,7 +39,7 @@ and 7 (a conflict, the hand-over, the close) is in
|
|
|
39
39
|
| A reply on an ask you follow | Interpret the reply in the issue's design context. Answer in its thread (`dispatch comment --reply-to <comment id>`; under an open ask whose next move is yours, such as the approval request you must revise or hand back, `--reply-to-ask <ask id> --turn agent`, since a default-turn reply hands that request back to the human and a corrected `--summary` is then refused), then adjust the plan or relay it via `envoy_publish` to the responsible worker's role token; scope and product decisions remain with you. |
|
|
40
40
|
| `worker-died` | Its `role` and `phase`. That role's claim failed: its launches or prompts ran out. For a phase worker the daemon holds the issue (phase `held`) and starts nothing more on it. Reassess the work, then decide with `retry_or_escalate`: `retry` when the failure looks agent-specific or transient, `escalate` to hand the held issue to the controller when it looks environmental. |
|
|
41
41
|
| `held` | Its `role` and `phase` (the phase the issue left), or `phase` with `reason: "escalated"`. Without `reason`, that phase's worker ran out of launches or prompts: the issue is held (phase `held`), the `worker-died` that comes with it is yours to answer with `retry_or_escalate` as that row says, and the controller hears of the hold too. With `reason: "escalated"`, it records your own `escalate`: the controller has the issue now, and you start nothing for it. |
|
|
42
|
-
| `ready-refused` | Its `version` and `reason
|
|
42
|
+
| `ready-refused` | Its `version` and `reason`, and the `reason` names the gated step the READY waits on. The merger's READY was refused, and its packet is kept with the completion so no second READY is ever needed. With `version` greater than 0, `reason` reads `READY refused: approve design version <N> of the tree's <step> document before requesting READY.`: that step's document is already registered at that version, so get it approved (`skill://legion-architect/references/design-gate.md`), and the daemon advances the merge and posts the kept packet the moment every gated step is open. With `version` 0, `reason` reads `READY refused: no design version of the tree's <step> document is approved; register and approve it before requesting READY.`: that step is not registered yet, so register it and get it approved (`skill://legion-architect/references/design-gate.md`) exactly as above; the daemon releases the kept packet the same way — at registration itself when that opens the step (a deployment that gates no step, or Dispatch already shows the version approved), otherwise the moment a human approves the version you registered. The refusal names the first closed step, in the order the deployment gates them, and no second READY is asked for however many steps were closed: read `gatedSteps` in your own record (the `legion` tool's `read_record`) for every step still closed, and open each one. Nothing else is needed once every gated step is open. On a root whose catch-up said `carried`, the gate is a copy on the old root's spec document: run nothing of `skill://legion-architect/references/design-gate.md` for that step; tell the controller the READY waits on a human's approval of that document, and `carried-gate-opened` follows the approval. A `reason` starting `READY_PACKET_MISSING` names a READY refused before the daemon kept packets: that READY is void and the issue stays in merging, so start it over (`park_child` then `rerun_child`, for a child) or end it. |
|
|
43
43
|
|
|
44
44
|
## A root that joins your tree, and a refused release
|
|
45
45
|
|
|
@@ -21,7 +21,7 @@ except a clear defect a role reports ([Issues handed to Legion](#issues-handed-t
|
|
|
21
21
|
- Keep no notes between turns, because a restarted session has only `read_state` and Dispatch.
|
|
22
22
|
- Reach the daemon only through the `legion` tool: `read_state`, and `set_status` to `todo`, `backlog` or `icebox`. Run no `legion` from bash, because the tool holds the controller grant.
|
|
23
23
|
- Never edit a repository, push a commit or call `handoff_complete`, and never forward a raw event to an architect, because the daemon runs the phases and raw events only fill the architect's context.
|
|
24
|
-
- Never open a design gate, because only a person's approval of the root's
|
|
24
|
+
- Never open a design gate, because only a person's approval in Dispatch opens one: the deployment gates the steps your `Design gate steps:` sentence names, each step records one document of the root issue (`decisions.md`, `verification.md` or the primary document), and the tree's work starts when a person has approved every one of them.
|
|
25
25
|
- When a `dispatch` command fails, or a `curl` read of Dispatch fails or prints no JSON array, act on nothing it would decide and name its first line in your summary (the turn's last message), because an empty read is no evidence. A Slack read answers a JSON object, and `skill://legion-controller/references/slack-mentions.md` holds its own success test.
|
|
26
26
|
- Before you say that a service or an API is out of reach, run the call through the secrets broker's client (`skill://agent-secrets`), and report it unreachable only with the command you ran and its output.
|
|
27
27
|
- Write every Dispatch comment, message and ask as `skill://dispatch-writing-specs`'s "Rules for the text" say.
|
|
@@ -97,7 +97,10 @@ an approval of a document that is not the root's primary one.
|
|
|
97
97
|
on the issue where the decision was made, has judged it: do nothing.
|
|
98
98
|
4. **Judge the words, not the kind.** Read the decision (`dispatch read --ref <ref>` for an ask or
|
|
99
99
|
a reply; for a request for changes, the notice's `reason` or the issue's `Spec approval:` line;
|
|
100
|
-
for an approval, `issues.<root>.designGate`
|
|
100
|
+
for an approval, `issues.<root>.designGate` and `issues.<root>.gatedSteps` beside it, which
|
|
101
|
+
carries one entry for each design gate step the deployment gates, with that step's
|
|
102
|
+
`artifactId`, `currentVersion`, `approvedVersion` and `status`). An approval or a request for
|
|
103
|
+
changes is two
|
|
101
104
|
events at one time, the approval request's `ask.answered` and `artifact.approved` or
|
|
102
105
|
`artifact.changes_requested`: judge them as one decision, whose `ref` is `dispatch://<KEY>/spec`.
|
|
103
106
|
- It gives the work a next step: an approval of the spec, a request for changes that says what
|
|
@@ -105,6 +108,9 @@ an approval of a document that is not the root's primary one.
|
|
|
105
108
|
the agent to go on. An approval counts when `issues.<root>.designGate.approvedVersion` equals
|
|
106
109
|
`issues.<root>.designGate.currentVersion`, and also when it is lower: then the architect's
|
|
107
110
|
start task names both versions, and the architect requests approval of the current one.
|
|
111
|
+
Where `gatedSteps` carries more than one entry, read each one the same way: the tree runs
|
|
112
|
+
again only once every entry is `approved`, and an entry still `waiting`, `unregistered` or
|
|
113
|
+
`changes_requested` is work the architect holds.
|
|
108
114
|
- It is your own hold (step 2): set the root to `todo` (`set_status`); admission queues it and
|
|
109
115
|
its architect resumes. Post one comment on the issue where the decision was made
|
|
110
116
|
(`dispatch comment --issue <KEY> --body-file -`): `Parked decision read: <ref> by <login>
|
|
@@ -68,9 +68,9 @@ Read this on a `triage` wake, at every start, and when a person hands you an iss
|
|
|
68
68
|
who will get this tree's questions and its design approval`, or, when `Assignee:` says
|
|
69
69
|
`unassigned`, `Unassigned — nobody's Inbox shows this tree's questions or its design approval
|
|
70
70
|
until someone takes it from the issue header (Assignee, beside Priority)`. Promise the design
|
|
71
|
-
approval only when your system prompt's `Design gate
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
approval only when your system prompt's `Design gate steps:` sentence names at least one step;
|
|
72
|
+
when it reads `Design gate steps: none.` nobody approves a design, so drop "and its design
|
|
73
|
+
approval" (and "or its design approval"). Take the assignee from the read, never
|
|
74
74
|
from how the issue was created, because an issue filed with no assignee still lands on the
|
|
75
75
|
project's default assignee where a person set one. An unassigned root still runs, but its
|
|
76
76
|
architect's asks reach only the Inbox's `Unassigned` filter.
|
|
@@ -132,7 +132,9 @@ Send any other to the owning architect: find its role in `read_state` and `envoy
|
|
|
132
132
|
mention's URL, its text and the issue key you checked. Never build a role token from a partial
|
|
133
133
|
issue reference, because a wrong token reaches nobody.
|
|
134
134
|
|
|
135
|
-
A person who asks how to let a root past its design gate approves
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
A person who asks how to let a root past its design gate approves, in Dispatch, the document of
|
|
136
|
+
each step the deployment gates, which your system prompt's `Design gate steps:` sentence names:
|
|
137
|
+
the `Approve` control in that document's header, or the approval question the architect's request
|
|
138
|
+
opened in the Inbox. The root's work starts when every one of those steps is approved. No
|
|
139
|
+
operator command opens a gate; a project that wants no gate sets `gates.design` to the empty list
|
|
140
|
+
(`design: []`) in `legion.yaml`.
|
|
@@ -145,7 +145,9 @@ reviewing until you finish.
|
|
|
145
145
|
the architect as a gap the tester and the reviewer named and neither refused, so the
|
|
146
146
|
architect's acceptance is the only gate on it: the acceptance comment names that gap and
|
|
147
147
|
decides there, accepting the issue or sending it back with the checks the architect writes,
|
|
148
|
-
which closes
|
|
148
|
+
which closes the root's `criteria` step where the deployment gates that step, since that step
|
|
149
|
+
records the root's Verification document and each new version of a step's document closes it;
|
|
150
|
+
a child's Verification document is gated by nothing. The architect also
|
|
149
151
|
reads the reviewer's own prose commits here, with the rest of the evidence, and that read is no
|
|
150
152
|
new round. It does not
|
|
151
153
|
accept while the issue holds a finding ask no controller has taken (`findings.md`, "When no
|
|
@@ -259,8 +261,10 @@ reviewing until you finish.
|
|
|
259
261
|
ordinary arm cannot merge the person-only head. Ordinary READY does not wait for the disarm. The
|
|
260
262
|
daemon tells that READY with the note at its head and the packet's `Merge:` line replaced, so the
|
|
261
263
|
issue and the merge queue role are never asked to arm it. A READY the design gate refused is
|
|
262
|
-
submitted by the daemon
|
|
263
|
-
|
|
264
|
+
submitted by the daemon once every gated step of the root's design gate is open, when the
|
|
265
|
+
approval that opens the last closed one moves the issue to `awaiting_merge`; the refusal named
|
|
266
|
+
one step and the version to approve, and no second READY is asked for however many steps held
|
|
267
|
+
it. A push that moves the head past a READY the gate holds voids it before then:
|
|
264
268
|
the architect is told `READY_HEAD_MOVED` and has the merger build a new READY at the new head.
|
|
265
269
|
The packet's second line records the step,
|
|
266
270
|
`Merge: the daemon submits pull request #<n> at <head> once READY is accepted`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjawhar/claude-envoy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"description": "Claude Code channel plugin for Legion's Envoy bus",
|
|
6
6
|
"type": "module",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"AGENTS.md"
|
|
20
20
|
],
|
|
21
21
|
"legion": {
|
|
22
|
-
"daemonApiVersion":
|
|
22
|
+
"daemonApiVersion": 55
|
|
23
23
|
},
|
|
24
24
|
"repository": {
|
|
25
25
|
"type": "git",
|
|
@@ -33,7 +33,7 @@ report, a disclosure, and a reply to a maintainer who wrote first.
|
|
|
33
33
|
|
|
34
34
|
Hold an extremely high bar. Optimise every upstream report, patch or public post for a human
|
|
35
35
|
reader and for being received well, so each one builds the company's reputation rather than a
|
|
36
|
-
|
|
36
|
+
reputation for low-quality or careless submissions.
|
|
37
37
|
|
|
38
38
|
## The four steps
|
|
39
39
|
|
|
@@ -95,7 +95,9 @@ review batch, never a per-message "can I send this" ask. Write its entry — wha
|
|
|
95
95
|
serious; who receives it and what they do; the exact text or patch; its evidence; a non-author
|
|
96
96
|
reviewer's APPROVE under your stream's reviewer-model rule; what the operator decides; its owner
|
|
97
97
|
— into the batch, add it under the batch's Waiting list, and tell the batch owner. Your stream's
|
|
98
|
-
brief names the batch and its owner.
|
|
98
|
+
brief names the batch and its owner. Where your stream's brief names no batch, ask the operator
|
|
99
|
+
for one, or hold the entry until there is a batch; a missing batch is not a reason to send a
|
|
100
|
+
per-message ask.
|
|
99
101
|
|
|
100
102
|
## What this skill does not cover
|
|
101
103
|
|