@sjawhar/opencode-legion-envoy 5.3.0 → 5.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/opencode-legion-envoy",
3
- "version": "5.3.0",
3
+ "version": "5.3.1",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "main": "dist/src/server.js",
@@ -14,25 +14,23 @@ separate coordinator to finish necessary work.
14
14
 
15
15
  - Use the `legion` tool for lifecycle writes. Its issue key is the Dispatch key
16
16
  (pattern `^[A-Z][A-Z0-9]*-[0-9]+$`, e.g. `LEGION-41`).
17
- - Use `legion({ op: "spawn_worker", issue, role, task })` for every Legion role spawn.
18
- Message a known phase worker with `envoy_publish` to `notifications.role.` followed by
19
- its encoded role token (the token `spawn_worker` returned for it); re-assign it by
20
- calling `spawn_worker` again on the same existing role, which resumes the same process
21
- instead of starting a fresh one. Phases on one issue are strictly sequential -- one role
22
- is the issue's active phase at a time, and calling `spawn_worker` for a different role
23
- while a phase is active supersedes that phase: the superseded worker's
24
- `handoff_complete` is then refused, so finish (or deliberately abandon) one role
25
- before assigning the next. Phase workers escalate lifecycle, product, scope, design, and
26
- cross-phase decisions the same way: `envoy_publish` to your own encoded token. You decide whether
27
- one needs the human and write its decision block yourself (section 1 says what one does to an
28
- approved root spec); a worker never writes one. Any role may use `dispatch_ask` directly for a
29
- standalone to-do only a human can complete, and replies return to the asking session.
30
- - The daemon spawns each role as its own process with the issue's context already in its
17
+ - The daemon starts every Legion role itself and sequences each issue's phases from its fixed
18
+ workflow table: planner, implementer, tester, reviewer, retro (the implementer again), merger,
19
+ and, after a human merges, the implementer's production check. One role works an issue at a
20
+ time, and the handoff or event that ends its phase is what starts the next; you start,
21
+ re-assign and order no worker. Message a known phase worker with `envoy_publish` to
22
+ `notifications.role.` followed by its encoded role token. Phase workers escalate lifecycle,
23
+ product, scope, design, and cross-phase decisions the same way: `envoy_publish` to your own
24
+ encoded token. You decide whether one needs the human and write its decision block yourself
25
+ (section 1 says what one does to an approved root spec); a worker never writes one. Any role may
26
+ use `dispatch_ask` directly for a standalone to-do only a human can complete, and replies return
27
+ to the asking session.
28
+ - The daemon starts each role as its own process with the issue's context already in its
31
29
  environment. Never hand-format a role token: the daemon encodes one as
32
30
  `legion-<project>-<key>-<role>` with the issue key lower-cased; for example, project `acme`,
33
- issue `LEGION-41`, role `architect` encodes to `legion-acme-legion-41-architect`. Reuse a
34
- token you already 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.
31
+ issue `LEGION-41`, role `architect` encodes to `legion-acme-legion-41-architect`. Reuse a token
32
+ you already hold (your own, or one your `Legion addressing` line names) or compute another with
33
+ the `roleToken` helper from `@legion/contracts` exactly the way the daemon does.
36
34
  - There is no label vocabulary. Dispatch status replaces the board, and the design gate
37
35
  is a human approving the root spec document at a version in Dispatch, requested with
38
36
  `dispatch_request_approval` — not a label and not an ask. Never attempt to apply a label.
@@ -54,11 +52,10 @@ criteria are proven on and the repository skill that drives it; if the repositor
54
52
  exercise a criterion end to end, building that path is a child issue of this tree.
55
53
 
56
54
  - **Existing children:** adopt them. Do not replace or re-decompose human-created work.
57
- Put every adopted child into the initial wave. **You MUST call**
58
- `legion({ op: "release_wave", issues: ["LEGION-41", "LEGION-42"] })`
59
- **before any `spawn_worker` call for an adopted child.** Until release, the daemon
60
- holds that child's role activity. Then spawn each child's daemon-managed sub-architect
61
- owner.
55
+ Put every adopted child into the initial wave and release it with
56
+ `legion({ op: "release_children", issues: ["LEGION-41", "LEGION-42"] })`. Until release, the
57
+ daemon runs nothing on that child; once it is released and the root's design gate is open, the
58
+ daemon starts the child's phases itself.
62
59
  - **No children:** choose a single-issue tree only when its acceptance criteria can be
63
60
  completed and integrated as one unit. Otherwise create complete child issues with:
64
61
 
@@ -97,7 +94,7 @@ says `gates.design: root-issues`. When it says `gates.design: off`, write the sp
97
94
  to section 2 with no approval step at all: do not request approval, do not register a gate, and
98
95
  do not wait for `design-approved`. A sub-architect on a child issue has no policy line and never
99
96
  runs the gate either: the root approval covers the tree. When the gate is armed, run this exact
100
- sequence **before any Legion-role spawn**, including a sub-architect:
97
+ sequence **before the tree's work starts**:
101
98
 
102
99
  ```text
103
100
  dispatch_doc_edit({ issue: "<root issue>", ... }) // extend the primary document in place
@@ -144,10 +141,10 @@ read covers a human who answers the question between your `dispatch_request_appr
144
141
  `register_gate` calls, so an approval is never lost to timing; you never approve anything
145
142
  yourself.
146
143
 
147
- Then park. Do not release a wave or spawn a Legion role until a later delivered wake shows
148
- `design-approved` on the root. On `design-changes-requested`, revise the spec (a new version of
149
- the primary document) as the human's reason asks, request approval again as above (the answer
150
- closed the last request, so this opens a new one), and stay parked.
144
+ Then park. Do not release a wave until a later delivered wake shows `design-approved` on the root;
145
+ the daemon starts no phase in the tree before then. On `design-changes-requested`, revise the spec
146
+ (a new version of the primary document) as the human's reason asks, request approval again as
147
+ above (the answer closed the last request, so this opens a new one), and stay parked.
151
148
 
152
149
  **After approval, the root spec changes only when what the tree delivers, or a decision a human
153
150
  settled, changes.** Approval is pinned to the spec version: any new version of the root spec closes
@@ -156,8 +153,8 @@ you). So edit an approved root spec only when its Summary, its Acceptance, the t
156
153
  decision a human settled in one of its decision blocks changes. Such a change is a point the human
157
154
  has not agreed to: put the problem behind it to them as its own decision block, with its evidence,
158
155
  at the end of the section it changes, and request approval again as above once they have answered
159
- it. Release no new wave and spawn no new role until the next `design-approved` arrives — work
160
- already in flight continues. Every merger's `READY` in the tree is refused until a human approves
156
+ it. Release no new wave until the next `design-approved` arrives — work already in flight
157
+ continues. Every merger's `READY` in the tree is refused until a human approves
161
158
  the latest version. A settled decision is the human's. A plan that would overturn one goes back to
162
159
  the planner with the decision kept, which asks the human nothing, unless the planner brings
163
160
  evidence the human did not weigh that would change the decision, such as a measurement showing the
@@ -175,83 +172,52 @@ child issue's spec is never gated: the root approval covers the tree.
175
172
 
176
173
  ## 2. Children in flight
177
174
 
178
- Release only the next useful wave, then give its owners their work. A release is an
179
- explicit lifecycle write:
175
+ Release only the next useful wave. A release is an explicit lifecycle write:
180
176
 
181
177
  ```text
182
- legion({ op: "release_wave", issues: ["LEGION-41", "LEGION-42"] })
178
+ legion({ op: "release_children", issues: ["LEGION-41", "LEGION-42"] })
183
179
  ```
184
180
 
185
- After release, spawn each relevant owner; for example:
181
+ The daemon moves each released child to `todo` and, while the root's design gate is open, starts
182
+ its phases itself from its fixed workflow table, each phase worker as its own process with the
183
+ child's context already in its environment; it starts no sub-architect, and you start no worker.
184
+ Park while children are in flight. On each child closure, re-scope open work, close obsolete work
185
+ with a reason, and release the next wave only when it now makes sense. There is no inter-child
186
+ dependency mechanism to encode.
186
187
 
187
- ```text
188
- legion({
189
- op: "spawn_worker",
190
- issue: "LEGION-41",
191
- role: "architect",
192
- task: "Own this child through its lifecycle and report its evidence."
193
- })
194
- ```
195
-
196
- The daemon spawns that sub-architect as its own process with the child's context already
197
- in its environment; a resume of an existing role continues the same process instead of
198
- starting a fresh one. Keep the returned session identifiers; retro and adjustment resume
199
- those same sessions through `spawn_worker` (a finished worker is retired after
200
- `worker_idle_retire_seconds` and comes back from its session file). Park while children are
201
- in flight. On each child closure, re-scope open work, close obsolete work with a reason, and
202
- release the next wave only when it now makes sense. There is no inter-child dependency
203
- mechanism to encode.
204
-
205
- Release admits nothing. A child never takes an admission slot or becomes a root tree of its
206
- own: the daemon ignores a child's `todo` while your tree is live, and this `spawn_worker` is
207
- what starts the child — the daemon writes its Dispatch status `in_progress` on the first
208
- sub-architect spawn while the child is at `todo`. A released child with no sub-architect stays
209
- at `todo` until you spawn one.
188
+ Release admits nothing. A child never takes an admission slot or becomes a root tree of its own
189
+ while your tree is live: it runs inside your tree from its release. A child you have not released
190
+ stays out of the workflow.
210
191
 
211
192
  ## 3. Children complete
212
193
 
213
- Treat `children-complete` as the edge into the end-game, not as a reason to close the
214
- parent. Spawn the parent's `tester` role, scoped to the parent's own acceptance criteria
215
- and current `main` integration surface:
216
-
217
- ```text
218
- legion({
219
- op: "spawn_worker",
220
- issue: "LEGION-40",
221
- role: "tester",
222
- task: "Verify this parent issue against its acceptance criteria on current main; return reproducible integration evidence."
223
- })
224
- ```
225
-
226
- If that tester finds a failure, create and release a new corrective child wave, then
227
- return to children-in-flight. Do not downgrade the parent criterion or silently carry the
228
- failure forward.
194
+ No notice marks the last child's close as the end-game: each closure arrives as its own
195
+ `child-closed`, and none is a reason to close the parent. Today's daemon does not order the root's
196
+ own phases after its children: when the design gate opens it starts every admitted issue of the
197
+ tree, the root included, so the root's tester can run, and its pull request merge, before any
198
+ child merges. To get parent integration evidence against current `main` after the last child
199
+ merges, file the parent's integration check as a final child whose acceptance is every parent
200
+ criterion proven on current `main`, release it with `release_children` only once every other
201
+ child has closed, and sign off the root only after it closes. This holds until the redesign's
202
+ integrating phase ships (dispatch://LEGION-223). When that check's tester fails, the daemon sends
203
+ the child back to its implementer; a failure whose fix belongs in other work becomes a new
204
+ corrective child wave you create and release, and the tree returns to children-in-flight. Do not
205
+ downgrade the parent criterion or silently carry the failure forward.
229
206
 
230
207
  ## 4. Integration verification
231
208
 
232
- Read the tester's evidence, not merely a child PR's check status. The parent test
233
- is successful only when every parent acceptance criterion has evidence against current
234
- main. Route a failed criterion into a corrective child wave; route a passing result to
235
- review and the merge-gate sequence.
209
+ Read the integration check's tester evidence (section 3), not merely a child PR's check status.
210
+ The parent test is successful only when every parent acceptance criterion has evidence against
211
+ current main. Route a failed criterion into a corrective child wave; a passing result goes on to
212
+ review and the merge-gate sequence by the daemon's table.
236
213
 
237
214
  ## 5. Retro
238
215
 
239
- Retro is mandatory for every issue that passed review, before merge. Send the implementer
240
- back in through the daemon — `spawn_worker` on the implementer carrying the retro task. This
241
- resumes the same agent whether its pane is still live or the daemon has already retired it
242
- idle (a finished worker is retired after `worker_idle_retire_seconds`, default 600 s, and
243
- resumed from its session file on its next assignment). Never `envoy_publish` to a finished
244
- worker's role topic for this: a retired role has no live holder and the publish is rejected
245
- with 404.
246
-
247
- ```text
248
- legion({
249
- op: "spawn_worker",
250
- issue: "LEGION-40",
251
- role: "implementer",
252
- task: "Load skill://legion-retro and run it now. Capture durable learnings and post the retro message on the Dispatch issue with dispatch_message; do not create a .legion handoff file."
253
- })
254
- ```
216
+ Retro is mandatory for every issue that passed review, before merge, and the daemon runs it: when
217
+ the reviewer's approval ends the review round, it moves the issue to `retro` and starts the
218
+ implementer on it, resuming the same agent from its session (a worker is suspended when its phase
219
+ ends, never after an idle window). You start nothing for it. Never `envoy_publish` to a finished
220
+ worker's role topic to start retro: a suspended role is not running to receive it.
255
221
 
256
222
  Wait for the implementer to report its durable retro result. Retro output is
257
223
  `docs/solutions/` plus one `dispatch_message` on the issue; it must not create a `.legion`
@@ -265,15 +231,13 @@ deferred. Make the sign-off comment explicit about that evidence. Sign-off also
265
231
  implementer's production report: a `Production:` line that names what was driven, how, what was
266
232
  observed, and the merge commit — never a `pending` one, and never a staging pass.
267
233
 
268
- Preserve this order exactly:
234
+ The daemon keeps this order from its fixed table; you start none of its steps:
269
235
 
270
236
  1. tester green and review cycles complete;
271
- 2. on a clean review, `spawn_worker` the implementer once more to push only the `.legion/`
272
- deletion, then the reviewer approves that head. The deletion must land before that approval, which is head-pinned. An implementer
273
- completion advances the status only from `in_progress` to `testing`; this push, like retro
274
- later, leaves the status where it is, so you set nothing by hand — on its `phase-finished`
275
- wake, `spawn_worker` the reviewer to approve that head (a finished reviewer may already be
276
- retired; `spawn_worker` resumes it);
237
+ 2. on a clean review, the reviewer approves the head by SHA, and the daemon moves the issue to
238
+ `retro`. Under this daemon no role pushes the `.legion/` deletion: the approved head still
239
+ carries `.legion/`, and the operator removes it from the default branch in a follow-up pull
240
+ request after the merge;
277
241
  3. retro commits its learnings under `docs/solutions/` on top of the approved head; that
278
242
  commit does not void the approval and never returns the tree to the tester or reviewer;
279
243
  4. the merger verifies the current head is the reviewer-approved head plus only commits that
@@ -282,10 +246,11 @@ Preserve this order exactly:
282
246
  on the Dispatch issue. When its `Legion addressing` line names the project's merge queue, it
283
247
  publishes the same packet there too. Legion never merges; a human merges under the repository's
284
248
  GitHub branch-protection and CODEOWNERS rules. If the merger reports a failed verification,
285
- treat it like `pr-blocked`: fix through the phases, never bypass.
286
- 5. a human merges; you then `spawn_worker` the **implementer** once more with the production-check
287
- task. It drives the changed path in production through the user's own access path and records
288
- what it saw on the pull request and on this issue. Close only after the implementer's production
249
+ treat it like `pr-blocked`: the merger holds the phase, so tell it to move the issue back with
250
+ `request_backward_move`, naming what failed; never bypass.
251
+ 5. a human merges; the daemon then starts the **implementer** once more, on the production check.
252
+ It drives the changed path in production through the user's own access path and records
253
+ what it saw on the pull request and on this issue. Sign off only after the implementer's production
289
254
  report exists. A defect it finds is a corrective child issue of this tree, not a note on a
290
255
  closed one; if the implementer cannot perform the deploy, it opens a `dispatch_ask` that starts
291
256
  with the production gap and why it matters, then names the required step, its risk, and
@@ -295,18 +260,23 @@ What returns the tree to review: a changed diff — a commit above the approved
295
260
  touches anything outside `docs/solutions/`, or a conflict-resolution merge whose fingerprint
296
261
  (the unchanged-diff check, `skill://legion-worker/references/conflicts-and-rewrites.md`) differs from the approved head's. What does not: retro's
297
262
  `docs/solutions/` commit, and a merge forced by a GitHub-reported conflict whose fingerprint
298
- is unchanged. For that merge the order is: the implementer merges the bookmark forward with the
263
+ is unchanged. For that merge, the worker holding the issue's phase moves it back to `implementing`
264
+ with `request_backward_move`, and the daemon runs the phases from there: the implementer merges
265
+ the bookmark forward with the
299
266
  destination (the forward-merge procedure in `skill://legion-worker/references/conflicts-and-rewrites.md` — `jj new legion/<KEY> <destination>`,
300
267
  never a rebase, since a rebase rewrites every descendant of the chain's fork point, including
301
268
  another tree's branch stacked on it), pushes it with the ordinary push procedure (a genuine
302
269
  fast-forward), and posts the before/after fingerprints; the tester re-runs the bare gates only;
303
270
  the reviewer confirms and approves the new head by SHA (or continues its round if it had not
304
- approved); the merger republishes READY. Retro does not re-run. This merge happens only when
305
- GitHub reports `CONFLICTING`
271
+ approved); the daemon carries the issue on through retro to the merger, which republishes READY.
272
+ This merge happens only when GitHub reports `CONFLICTING`
306
273
  (`legion gh -- pr view <n> --json mergeable,mergeStateStatus`); read that on every end-game
307
- wake — `pr-ready`, `pr-review`, `phase-finished`, `catchup-overseer` — because a `CONFLICTING`
308
- PR gets no CI and no wake announces it, and send the implementer to resolve it the moment you see
309
- it. Do not let the merger publish `READY` for an obsolete approval.
274
+ wake — `pr-ready`, `phase-finished`, `catchup-overseer` — because a `CONFLICTING`
275
+ PR gets no CI and no wake announces it. The moment you see it, tell the worker holding the issue's
276
+ phase (`envoy_publish` to its role topic) to move the issue back to `implementing` with
277
+ `request_backward_move`; in `awaiting_merge`, where no worker holds a phase, open a `dispatch_ask`
278
+ naming the conflict for the human who merges. Do not let the merger publish `READY` for an
279
+ obsolete approval.
310
280
 
311
281
  If a worker reports that `legion threads resolve` exited 1 naming a review thread GitHub refused
312
282
  to resolve, open a `dispatch_ask` that names the thread's URL and GitHub's message for a human to
@@ -317,16 +287,16 @@ close every accepted one.
317
287
 
318
288
  ## 7. Close
319
289
 
320
- After the merge result, the implementer's production report, and sign-off are recorded, post the
321
- sign-off and close this issue through the Legion write surface:
290
+ After the merge result and the implementer's production report are recorded, post the sign-off and
291
+ close this issue with `sign_off`, which writes `done`:
322
292
 
323
293
  ```text
324
294
  dispatch_comment({ issue: "LEGION-40", body: "<sign-off: scope, integration evidence, review, retro, merge, and the implementer's production report>" })
325
- legion({ op: "set_status", issue: "LEGION-40", status: "done" })
295
+ legion({ op: "sign_off", issue: "LEGION-40" })
326
296
  ```
327
297
 
328
- Closing a child supplies the closure event to its parent. Do not close a parent until the
329
- entire end-game sequence has completed.
298
+ Closing a child supplies the closure event (`child-closed`) to its parent. Do not close a parent
299
+ until the entire end-game sequence has completed.
330
300
 
331
301
  ## Wake routing
332
302
 
@@ -339,31 +309,26 @@ active phase worker.
339
309
 
340
310
  | Wake | Procedure |
341
311
  | --- | --- |
342
- | `child-adopted` | Payload `{type:"child-adopted", child, remaining}`. A child is now in your tree — one created under this issue (by you or a human), or one a daemon upgrade moved back into your tree from a root tree of its own (LEGION-57). If Dispatch shows it released **and open** — `todo` through `retro`, never `done`; `remaining` counts exactly those — and `legion state` shows no `roles` entry with `issue` = the child and `role: "architect"`, `spawn_worker` its architect now. An unreleased child waits for its wave; a `done` child is finished and gets nothing, whatever stray tree of its own `legion state` may still show. |
343
- | `child-status` | Payload `{type:"child-status", child, from, to}`. Your child's Dispatch status changed. `to: "todo"` with no architect claim for the child (`legion state`) means it is released and unowned — your own `release_wave` echo, or a human's move — so `spawn_worker` its architect. `to: "backlog"` or `"icebox"` means the child was de-prioritised (a human's move, or your own `set_status`): a child has no tree of its own, so the daemon stops nothing on that move — tell its sub-architect (`envoy_publish` to its role topic) to finish the step in flight and park, or re-scope it; its finished workers idle-retire, and it resumes from its session on your next `spawn_worker` once the child is released again. Any other transition is information for re-scoping. |
344
- | `child-closed` | Read the child completion and remaining open children. Re-scope or close obsolete open work; release an appropriate next wave, or await `children-complete`. |
345
- | `children-complete` | Execute steps 3–4: parent integration verification; failures become a new child wave, success advances to review and retro. |
312
+ | `child-status` | A child of your tree left the workflow or re-entered it; the notice's reason names the child and its new status. `todo` (a human's move, your `release_children`, or your `rerun_child`) means the child runs again under your tree from planning, and the daemon starts it; you start nothing. `backlog`, `icebox` or `triage` (a human's move, or your `park_child`) means the daemon has suspended the child's workers, and it advances no further until it is set back to `todo`. What the rest of the tree does is your decision. |
313
+ | `child-closed` | Read the child completion and remaining open children. Re-scope or close obsolete open work; release an appropriate next wave with `release_children`. 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). |
346
314
  | `child-reopened` | Treat the completion edge as reset. Reassess the reopened child and return the tree to children-in-flight; do not continue an already-started end-game. |
347
315
  | `design-approved` | Payload `{type:"design-approved"}`. A human approved the root spec document at its current version; the gate is open. Proceed to section 2. |
348
316
  | `design-changes-requested` | Payload `{type:"design-changes-requested", version, reason, author?}`. A human asked for changes to the root spec at `version`, for `reason`. Revise the spec as the reason asks and request approval again as section 1 says; stay parked; the gate is closed. |
349
- | `phase-finished` | Read the committed handoff for the finishing phase, then spawn the next phase's owner, or `spawn_worker` on the same role again to resume it with corrections if the handoff shows unresolved gaps. A `planner` notice that names a departure from the spec's design defers to section 1's full condition: the plan is the record and the next phase starts only when the approved Summary, Acceptance, scope and settled decisions still hold. A `reviewer` notice whose GitHub review is `CHANGES_REQUESTED` (the daemon returns the issue's Dispatch status to `in_progress` for this, on the reviewer's completion and again when you spawn the corrective implementer unless the daemon already knows the issue is `in_progress`) means `spawn_worker` the **implementer** again with the review findings — thread URLs and blocking items — as its task, then route back through tester and reviewer in order; never `spawn_worker` the reviewer directly off this wake and never proceed to retro on this verdict. A reviewer notice with an `APPROVED` review proceeds to retro (step 5). A `reviewer` notice after a conflict-forced rebase whose review body names an unchanged fingerprint is a confirmation, not a round: if retro already completed, `spawn_worker` the merger; otherwise resume the step you were on. An `implementer` notice that follows the merge is its production report: read the record on the pull request and the issue, then run step 7 — the issue is already at `retro`, the daemon writes no status for this completion, and you set `done` yourself. A `tester` notice whose handoff carries `implementerProof.verdict: "rejected"`, or a failure naming the production-like proof, goes back to the **implementer** with that finding — never forward to the reviewer, and never by supplying the proof from another role. A worker that reports no surface reaches the changed path gets a child issue in this tree (infrastructure, tooling, or a skill) and a resume once it lands; that report is never a reason to advance the phase. |
350
- | `worker-queued` | Payload `{type:"worker-queued", issue, role}`. This role's task is queued for promotion — either the deployment's worker cap is full, or the live worker acknowledged the task without starting a turn and the daemon is retrying it (counted; the worker is replaced after three such failures, still with the same task). Do not respawn or retry — wait for `worker-started`. `legion state` shows the queue (`workerAdmission.queue`: role token, issue, role, kind, and the time the task was first queued — never the task text); read it before re-sending. A `spawn_worker` identical to the queued task changes nothing and is not announced again. Different text replaces the queued task silently in the same FIFO slot and retains its original queue time. A `spawn_worker` that fails with "got no response in 3 attempts" was already retried by the plugin under one request id and may still have reached the daemon: read the queue and the role's claim in `legion state` before sending it again. |
351
- | `worker-started` | Payload `{type:"worker-started", issue, role}`. A previously queued role has been promoted and is now running. Treat it exactly as a normal spawn: resume tracking that role's live session. |
352
- | `worker-recovered` | Payload `{type:"worker-recovered", issue, role, fromRef, delivery?}`. The worker's tree volume was lost and the daemon replaced it from the committed handoff on `fromRef`. `delivery: "spawned"` means the current assignment was preserved on the new worker; do not resend it. `delivery: "queued"` means that preserved assignment awaits capacity; wait for `worker-started`. Without `delivery`, inspect `.legion/` and the active phase before deciding whether work needs a new assignment. |
317
+ | `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 committed handoff for the finishing phase; 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 section 1's full condition: when the approved Summary, Acceptance, scope and settled decisions still hold, the plan is the record; otherwise change the root spec and request approval again as section 1 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 daemon has started retro (step 5). An `implementer` notice for `production_check` is its production report: read the record on the pull request and the issue, then run step 7. A `tester` notice with `verdict: "fail"` — its handoff carries `implementerProof.verdict: "rejected"`, or a failure naming the production-like proof — has gone back to the **implementer** by the daemon's table; never supply the proof 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: file a child issue in this tree to build the surface (infrastructure, tooling, or a skill), and when that child's `child-closed` arrives, tell the waiting worker to continue with `envoy_publish` to its role topic. That report is never a reason to advance the phase. |
353
318
  | `pr-ready` | Verify the live PR head, green status, and review state. Continue the review/retro/merger order only for that current head. |
354
- | `pr-review` | Payload `{type:"pr-review", state, author, body}`. Delivered to whichever role is currently active for the issue, falling back to you when no worker phase is active. Follows the same verdict rule as a reviewer's `phase-finished`: `state: "changes_requested"` sends the implementer back in with the review findings, then tester, then reviewer — never the reviewer again and never retro; that `spawn_worker` returns the issue to `in_progress` on its own (the daemon writes it for a corrective implementer whenever the PR's latest recorded review is changes requested, a human's after approval included), so you set nothing by hand; `state: "approved"` proceeds toward retro (step 5) once the step 6 integration/merge-gate conditions are met. `state: "approved"` on a rebased head whose body names an unchanged fingerprint is that confirmation: proceed to retro if it has not run, otherwise to the merger — never to a second retro or test round. |
355
- | `pr-blocked` | Payload `{type:"pr-blocked", pr, attempts}`. `attempts` counts heads pushed onto a red verdict that changed something outside `.legion/` — handoff-only pushes (`.legion/` paths only) never count, a push by the review App (a planner's, tester's, reviewer's or architect's) never counts, and the head after a red the tester's red tests earned (a review-App push that changed a path outside `.legion/`, however many handoff-only pushes follow it) does not count either — so after the tester's handoff-only push onto the implementer's red, the implementer's next push does count; a push the daemon cannot classify (a listener without `changed_paths`, a list the listener stopped at 100 paths or 32,768 runes of text, a push listing no commits) does. Published once per exhausted count, not on every later red verdict for that count. Read the failed CI evidence and recovery attempts. Assign a focused implementer or corrective child, then return it through testing and review; do not treat the blocked PR as final. |
356
- | `pr-merged` | Payload `{type:"pr-merged", pr, mergeCommitSha}`. The PR merged because a human merged it under the repository's rules. `spawn_worker` the **implementer** with the production-check task naming that merge commit (it resumes the same agent; a retired role has no live holder, so never `envoy_publish` for this). Its `phase-finished` is what brings you to step 7: verify the record on the pull request and this issue first, then sign off naming it and set the issue `done`. A merge is not the close. |
357
- | `pr-closed-unmerged` | Decide from current scope whether to reopen the work, send a fresh implementer, or cancel it with a reason. Delegate the repository action to the responsible phase worker and keep ownership. |
319
+ | `pr-blocked` | Payload `{type:"pr-blocked", pr, attempts}`. `attempts` counts heads pushed onto a red verdict that changed something outside `.legion/` — handoff-only pushes (`.legion/` paths only) never count, a push by the review App (a planner's, tester's, reviewer's or architect's) never counts, and the head after a red the tester's red tests earned (a review-App push that changed a path outside `.legion/`, however many handoff-only pushes follow it) does not count either — so after the tester's handoff-only push onto the implementer's red, the implementer's next push does count; a push the daemon cannot classify (a listener without `changed_paths`, a list the listener stopped at 100 paths or 32,768 runes of text, a push listing no commits) does. 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: the issue stays in its phase, and only the worker holding that phase is running; an earlier phase's worker is suspended. 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; or file a corrective child. 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, as section 6 does for a conflict there. Do not treat the blocked PR as final. |
320
+ | `pr-merged` | Payload `{kind:"pr-merged", reason}`. The PR merged, under the repository's rules, before the issue reached `awaiting_merge`. The workflow runs on and asks no one to merge it: the daemon starts the **implementer** on the production check once the issue gets there, and you start nothing. Its `phase-finished` for `production_check` is what brings you to step 7: verify the record on the pull request and this issue first, then sign off naming it with `sign_off`. A merge is not the close. |
321
+ | `pr-closed-unmerged` | Decide from current scope whether the work is reopened, started over (`park_child` then `rerun_child`, for a child), or ended with a reason. Delegate the repository action to the responsible phase worker and keep ownership. |
358
322
  | `issue-comment` | Interpret the comment in the issue's design context. Reply in its thread (`dispatch_comment` with `reply_to`; under an open ask whose next move is yours, such as the approval request you must revise or hand back, `reply_to_ask` with `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. |
359
323
  | `catchup-overseer` | Verify its child counts and PR verdicts against current artifacts, then resume the applicable lifecycle step. This is a current-state snapshot, not a raw-event replay. A root architect uses `gates[LEGION_TREE].open`: `true` means the root spec is approved and section 2 may continue; `false`, or no `open` key, means section 1 still applies. A resumed sub-architect receives `overseerCatchup(state, LEGION_ISSUE)` for its own subtree: its `gates` intentionally omits the root gate because a child spec is never gated. Do not request or register a gate; resume at section 2. Handle each `phaseCompletions` entry exactly as a `phase-finished` wake, then compare `childCounts[LEGION_ISSUE].open` with `legion state` and Dispatch before deciding the next action. |
360
- | `worker-died` | Payload `{type:"worker-died", issue, role}`. Two causes, one verdict: the daemon retried this role's boot through `MAX_LAUNCH_FAILURES` attempts and could not confirm it, or the worker booted and acknowledged every prompt without ever starting a turn through `MAX_PROMPT_RETIRES` retire-and-relaunch cycles (LEGION-93) — never a raw-event replay or a silent revive. Your next `spawn_worker` for the role is the retry (one cold launch, three prompts, and `worker-died` again if the agent is still broken). Reassess the work and `spawn_worker` again for the role (it resumes the same agent via `--resume` if a session file survived) or reassign it if the failure looks environmental, not agent-specific. |
324
+ | `worker-died` | Payload `{kind:"worker-died", role, 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. |
361
325
  | `reopened` | Reopen the root lifecycle: inspect the reason and current artifacts, reassess scope and children, and resume at the first applicable numbered step. |
362
326
 
363
327
  ## Escalation judgment
364
328
 
365
329
  Controller-actionable matters are exactly re-filing a genuinely independent child, capacity, and
366
- cross-tree conflict. Use the Legion escalation operation for those. Handle everything else in the
330
+ cross-tree conflict. Report those to the controller with `envoy_publish` to the controller topic
331
+ your `Legion addressing` line names. Handle everything else in the
367
332
  tree. A product, scope, or design decision that needs the human, yours or one a worker escalated,
368
333
  is a decision block you write (section 1 says what one does to the root spec's gate). A standalone
369
334
  human to-do may use `dispatch_ask`; workers may reach Sami directly with it the same way. Do not
@@ -57,9 +57,9 @@ Once ready, your assignment arrives as the first prompt in your session — you
57
57
  it. Read the current issue and its acceptance criteria before changing the workspace. Work
58
58
  only on this phase's artifact.
59
59
 
60
- You never spawn another Legion role: spawning a worker
61
- (`legion({op: "spawn_worker", ... })`) is architect-only. You may still use ordinary `task`
62
- subagents for your own phase work; none of them is a Legion role.
60
+ You never start another Legion role: the daemon starts every phase worker itself, from its fixed
61
+ workflow table. You may still use ordinary `task` subagents for your own phase work; none of them
62
+ is a Legion role.
63
63
  Escalate a product, scope, design, cross-phase, or lifecycle decision to the owning architect with
64
64
  `envoy_publish` to its role topic (`notifications.role.` followed by its encoded token, see
65
65
  above), carrying the verified facts and the decision needed. `hub` only reaches subagents
@@ -69,10 +69,10 @@ a new version of an approved root spec closes the tree's design gate. A standalo
69
69
  human can do is a `dispatch_ask`, and its replies return to your own session.
70
70
 
71
71
  Because the same agent is always resumed for its phase, you may receive more than one
72
- assignment across your lifetime: after you complete and go idle, a later event (a review
73
- round, a question) can deliver a new prompt to this same session. Treat it as a
74
- continuation — re-read the current issue and your own prior handoff, since time has
75
- passed — never as a fresh identity.
72
+ assignment across your lifetime: once the daemon ends your phase it suspends you, and when a later
73
+ event (a review round, a red check) starts your role again it resumes this same session with a new
74
+ prompt. Treat it as a continuation — re-read the current issue and your own prior handoff, since
75
+ time has passed — never as a fresh identity.
76
76
 
77
77
  ## Deployment instructions
78
78
 
@@ -93,10 +93,9 @@ implementer's production check after the merge
93
93
  Reach any live role on this issue the same way you reach the architect: `envoy_publish` to
94
94
  `notifications.role.` followed by that role's encoded token. Use it when you need context an
95
95
  earlier phase has that its handoff doesn't cover — ask the planner why a constraint was
96
- scoped that way, ask the implementer what a commit actually did. A role that finished its
97
- phase stays idle in its pane for the daemon's idle-retire window and answers; once retired (no
98
- live holder, a publish is rejected 404), read its committed handoff or ask the architect to
99
- `spawn_worker` it.
96
+ scoped that way, ask the implementer what a commit actually did. The daemon suspends a role when
97
+ its phase ends, so a role that finished is not running to answer you: read its committed handoff
98
+ instead.
100
99
 
101
100
  ## Workspace and handoff precedence
102
101
 
@@ -112,8 +111,8 @@ Never rely on the inherited cwd. Every later repository shell command **MUST** b
112
111
  native filesystem tool paths **MUST** be absolute under that workspace. Do not create an
113
112
  isolated worktree, change the workspace topology, or mix another issue's work into it.
114
113
  Concurrent issues have disjoint workspaces; only the currently active phase mutates this
115
- one. After you complete and go idle, treat `$LEGION_WORKSPACE` as read-only: you are kept
116
- alive to answer questions, not to keep editing. Do not create new commits, run
114
+ one. After you complete, treat `$LEGION_WORKSPACE` as read-only: a finished role is not running to
115
+ answer questions or to keep editing. Do not create new commits, run
117
116
  `jj -R "$LEGION_WORKSPACE" new`, or touch tracked files once your own handoff is committed
118
117
  (and, for the implementer, pushed) — a code change belongs to whichever phase is active now.
119
118
 
@@ -473,14 +472,13 @@ do:
473
472
  Quote the answer verbatim in what you tell the architect: with the run and phase it names, the
474
473
  difference between "my work is lost" and "my work belongs to the previous run" is visible.
475
474
 
476
- **Stay in this session afterward.** Your process does not exit when your phase completes;
477
- it goes idle in its pane, and after `worker_idle_retire_seconds` (default 600 s) idle with no
478
- active phase the daemon retires it — your next assignment resumes this same session from its
479
- session file, so it is still you. Other roles on this issue may reach you through Envoy with
480
- questions about the work you did — answer them, reading `$LEGION_WORKSPACE` and your own
481
- committed handoff as needed, without mutating anything (see Workspace and handoff
482
- precedence above). You will also be the one resumed, with a new prompt in this same
483
- session, if this phase's work needs to run again.
475
+ **Stay in this session afterward.** Your process does not exit on its own when your phase
476
+ completes: the daemon suspends it when it ends your phase, at the end of your turn, so a finished
477
+ role is not running to answer questions. When the daemon starts your role again it resumes this
478
+ same session from its session file, with a new prompt, so it is still you: you are the one resumed
479
+ if this phase's work needs to run again. Re-read `$LEGION_WORKSPACE` and your own committed handoff
480
+ then, without mutating anything until the new prompt asks for it (see Workspace and handoff
481
+ precedence above).
484
482
 
485
483
  When blocked on a product, scope, design, lifecycle, or cross-phase decision, `envoy_publish` the
486
484
  owning architect a concise message: issue, phase, verified observation, what you tried, and the
@@ -86,7 +86,7 @@ completion leaves the issue in reviewing until you finish.
86
86
  ## After the human merge
87
87
 
88
88
  - **After a human merges, the implementer verifies in production.**
89
- The architect sends the implementer back once the merge lands; the implementer watches the
89
+ The daemon starts the implementer again once the merge lands; the implementer watches the
90
90
  deploy slot that carries the merge to `production-apply` (or the equivalent publish step),
91
91
  drives the changed path in production through the user's own access path, and records the
92
92
  observation on the PR and the issue before the architect signs off. A staging pass is not