@sjawhar/opencode-legion-envoy 5.6.1 → 5.6.2
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 +1 -1
- package/skills/AGENTS.md +1 -1
- package/skills/legion-architect/SKILL.md +35 -37
- package/skills/legion-controller/SKILL.md +40 -73
- package/skills/legion-retro/SKILL.md +19 -18
- package/skills/legion-worker/SKILL.md +31 -68
- package/skills/legion-worker/references/merge-gate.md +29 -33
- package/skills/legion-worker/references/pr-body.md +15 -16
- package/skills/legion-worker/references/review-threads.md +4 -5
package/package.json
CHANGED
package/skills/AGENTS.md
CHANGED
|
@@ -6,7 +6,7 @@ event intake, process lifecycle, credentials, and role delivery.
|
|
|
6
6
|
|
|
7
7
|
| Skill | Who reads it | What it owns |
|
|
8
8
|
| --- | --- | --- |
|
|
9
|
-
| `ce-simplify-code/` | the implementer, once per pull request | the behaviour-preserving simplify pass
|
|
9
|
+
| `ce-simplify-code/` | the implementer, once per pull request | the behaviour-preserving simplify pass, in the implementer's first implementing round (Legion's copy of the MIT-licensed Compound Engineering skill; `LICENSE` beside it) |
|
|
10
10
|
| `dispatch/` | every role, and any session writing to Dispatch | specs, asks, comments, artifacts, and messages on native Dispatch |
|
|
11
11
|
| `dispatch-brainstorming/` | any session with Dispatch that starts a design conversation or writes a plan, as `dispatch-first` directs | the design conversation in the issue's spec, turn by turn to one approval, and the plan as the issue's `plan.md`; in a session with Dispatch it replaces superpowers' `brainstorming` and `writing-plans` |
|
|
12
12
|
| `dispatch-first/` | every session with Dispatch configured, injected by each host plugin on every request (Oh My Pi), on every `SessionStart` (startup, resume, clear, compact, fork) and `SubagentStart` (Claude Code), or as an instruction file (OpenCode) | searching Dispatch before acting, extending the existing issue, citing decisions, closing duplicates; kept under 60 lines and 6,000 characters |
|
|
@@ -77,24 +77,24 @@ exercise a criterion end to end, building that path is a child issue of this tre
|
|
|
77
77
|
answers the tree's asks); under a personal token it goes to that token's owner, whom
|
|
78
78
|
`dispatch_whoami` names. Set it only when a human told you a specific person owns that child.
|
|
79
79
|
|
|
80
|
-
Specifications written into Dispatch follow `skill://dispatch`'s
|
|
80
|
+
Specifications written into Dispatch follow `skill://dispatch`'s "Writing a spec" section.
|
|
81
81
|
Wave releases, child closures, and your own status are visible from the issue tree and the
|
|
82
|
-
handoffs; do not narrate them into the spec or a `dispatch_message`. A to-do only a human can
|
|
83
|
-
is a `dispatch_ask`.
|
|
82
|
+
handoffs; do not narrate them into the spec or a `dispatch_message`. A to-do only a human can
|
|
83
|
+
clear is a `dispatch_ask`.
|
|
84
84
|
|
|
85
85
|
The issue's primary document **is** the root specification. Extend it in place: a new version
|
|
86
86
|
that adds only the evidence each decision needs and what the human decides, each as a
|
|
87
|
-
|
|
87
|
+
decision block (`skill://dispatch`, "Decision blocks"). The decomposition and its waves, how each
|
|
88
88
|
outcome is proven, and the integration test are your own calls: they go in the child issues and
|
|
89
89
|
the planner's `.legion/plan.json`, not the root spec. Never post a second "spec" artifact beside
|
|
90
90
|
it (`dispatch_artifact` with the primary document's name replaces the human's document; do not do
|
|
91
91
|
that).
|
|
92
|
-
The design gate runs only when the
|
|
93
|
-
says `gates.design: root-issues`. When it says `gates.design: off`, write
|
|
94
|
-
|
|
95
|
-
do not wait for `design-approved
|
|
96
|
-
|
|
97
|
-
sequence **before the tree's work starts**:
|
|
92
|
+
The design gate's approval step runs only when the `Design gate policy` sentence of your
|
|
93
|
+
`Legion addressing` line says `gates.design: root-issues`. When it says `gates.design: off`, write
|
|
94
|
+
the spec and register it with `register_gate` at its current version, with no approval step: do
|
|
95
|
+
not request approval and do not wait for `design-approved`; the gate opens at registration. A
|
|
96
|
+
sub-architect on a child issue has no policy line and never runs the gate: the root approval
|
|
97
|
+
covers the tree. When the gate is armed, run this exact sequence **before the tree's work starts**:
|
|
98
98
|
|
|
99
99
|
```text
|
|
100
100
|
dispatch_doc_edit({ issue: "<root issue>", ... }) // extend the primary document in place
|
|
@@ -112,7 +112,7 @@ legion({
|
|
|
112
112
|
```
|
|
113
113
|
|
|
114
114
|
The decision blocks come first: settle every one as
|
|
115
|
-
|
|
115
|
+
"Approval of a spec" (`skill://dispatch`) says before you request approval;
|
|
116
116
|
`dispatch_request_approval` refuses while one is open. Each answer reaches you, since you follow
|
|
117
117
|
every ask you open. An approval request carries nothing new: request it only once the human has
|
|
118
118
|
agreed to every point in the spec, so a point they have not agreed to gets its own decision block
|
|
@@ -127,8 +127,8 @@ ordinary question is not a gate and the daemon ignores its answer. Copy `artifac
|
|
|
127
127
|
spec.md (document id <UUID>) at version <N> (ask <id>)", followed by the question the human's
|
|
128
128
|
Inbox shows, and its `details.artifact` / `details.version` carry the same two values. The
|
|
129
129
|
document id is never the slug or file name you passed in (`spec`, `spec.md`): the daemon
|
|
130
|
-
recognizes the document's approval events by that id
|
|
131
|
-
|
|
130
|
+
recognizes the document's approval events by that id and takes no other value; the `legion` tool
|
|
131
|
+
looks a slug or file name up in Dispatch and hands the daemon the id. Calling `dispatch_request_approval` again while that request
|
|
132
132
|
waits on the human, with the same `summary`, changes nothing and returns it (its text says
|
|
133
133
|
it "already waits on the human"), so it is safe to repeat; a different `summary` is refused then.
|
|
134
134
|
A newer version of the spec moves the open request to that version and leaves it waiting on you,
|
|
@@ -181,8 +181,9 @@ legion({ op: "release_children", issues: ["LEGION-41", "LEGION-42"] })
|
|
|
181
181
|
The daemon moves each released child to `todo` and, while the root's design gate is open, starts
|
|
182
182
|
its phases itself from its fixed workflow table, each phase worker as its own process with the
|
|
183
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,
|
|
185
|
-
|
|
184
|
+
Park while children are in flight. On each child closure, re-scope open work, take obsolete work
|
|
185
|
+
out of the workflow with `park_child` (saying why on the issue with `dispatch_comment`), and
|
|
186
|
+
release the next wave only when it now makes sense. There is no inter-child
|
|
186
187
|
dependency mechanism to encode.
|
|
187
188
|
|
|
188
189
|
Release admits nothing. A child never takes an admission slot or becomes a root tree of its own
|
|
@@ -221,7 +222,7 @@ worker's role topic to start retro: a suspended role is not running to receive i
|
|
|
221
222
|
|
|
222
223
|
Wait for the implementer to report its durable retro result. Retro output is
|
|
223
224
|
`docs/solutions/` plus one `dispatch_message` on the issue; it must not create a `.legion`
|
|
224
|
-
file or rewrite the reviewer-approved head
|
|
225
|
+
file or rewrite the reviewer-approved head.
|
|
225
226
|
|
|
226
227
|
## 6. Architect sign-off and merge
|
|
227
228
|
|
|
@@ -235,16 +236,15 @@ The daemon keeps this order from its fixed table; you start none of its steps:
|
|
|
235
236
|
|
|
236
237
|
1. tester green and review cycles complete;
|
|
237
238
|
2. on a clean review, the reviewer approves the head by SHA, and the daemon moves the issue to
|
|
238
|
-
`retro`.
|
|
239
|
-
|
|
240
|
-
request after the merge;
|
|
239
|
+
`retro`. No role pushes a `.legion/` deletion: the approved head still carries `.legion/`, and
|
|
240
|
+
the operator removes it from the default branch in a follow-up pull request after the merge;
|
|
241
241
|
3. retro commits its learnings under `docs/solutions/` on top of the approved head; that
|
|
242
242
|
commit does not void the approval and never returns the tree to the tester or reviewer;
|
|
243
243
|
4. the merger verifies the current head is the reviewer-approved head plus only commits that
|
|
244
244
|
change `docs/solutions/` (`jj diff --from <approved-sha> --to <tip-sha> --summary`, quoted in
|
|
245
|
-
READY) and
|
|
246
|
-
|
|
247
|
-
publishes the
|
|
245
|
+
READY) and sends the READY packet with its completion; the daemon posts
|
|
246
|
+
`READY #<n> at <current sha> (approved at <approved sha>) for <KEY> (<pr url>)` on the Dispatch
|
|
247
|
+
issue and publishes it to the project's merge queue role when one is set. Legion never merges; a human merges under the repository's
|
|
248
248
|
GitHub branch-protection and CODEOWNERS rules. If the merger reports a failed verification,
|
|
249
249
|
treat it like `pr-blocked`: the merger holds the phase, so tell it to move the issue back with
|
|
250
250
|
`request_backward_move`, naming what failed; never bypass.
|
|
@@ -268,19 +268,19 @@ never a rebase, since a rebase rewrites every descendant of the chain's fork poi
|
|
|
268
268
|
another tree's branch stacked on it), pushes it with the ordinary push procedure (a genuine
|
|
269
269
|
fast-forward), and posts the before/after fingerprints; the tester re-runs the bare gates only;
|
|
270
270
|
the reviewer confirms and approves the new head by SHA (or continues its round if it had not
|
|
271
|
-
approved); the daemon carries the issue on through retro to the merger,
|
|
271
|
+
approved); the daemon carries the issue on through retro to the merger, whose new READY packet the daemon posts.
|
|
272
272
|
This merge happens only when GitHub reports `CONFLICTING`
|
|
273
273
|
(`legion gh -- pr view <n> --json mergeable,mergeStateStatus`); read that on every end-game
|
|
274
|
-
wake — `
|
|
274
|
+
wake — `phase-finished`, `catch-up`, `checks-red`, `review-stuck` — because a `CONFLICTING`
|
|
275
275
|
PR gets no CI and no wake announces it. The moment you see it, tell the worker holding the issue's
|
|
276
276
|
phase (`envoy_publish` to its role topic) to move the issue back to `implementing` with
|
|
277
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
|
|
278
|
+
naming the conflict for the human who merges. Do not let the merger send a READY packet for an
|
|
279
279
|
obsolete approval.
|
|
280
280
|
|
|
281
281
|
If a worker reports that `legion threads resolve` exited 1 naming a review thread GitHub refused
|
|
282
282
|
to resolve, open a `dispatch_ask` that names the thread's URL and GitHub's message for a human to
|
|
283
|
-
resolve it by hand, with options for resolved / could not; the merger does not
|
|
283
|
+
resolve it by hand, with options for resolved / could not; the merger does not complete while it
|
|
284
284
|
is open. That is the one review-thread step a human takes: the review App cannot resolve a thread
|
|
285
285
|
on a pull request the implementer opened, and the implementer's and merger's runs of the command
|
|
286
286
|
close every accepted one.
|
|
@@ -317,19 +317,17 @@ active phase worker.
|
|
|
317
317
|
| Wake | Procedure |
|
|
318
318
|
| --- | --- |
|
|
319
319
|
| `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. |
|
|
320
|
-
| `child-closed` | Read the child completion and remaining open children. Re-scope
|
|
321
|
-
| `
|
|
322
|
-
| `design-
|
|
323
|
-
| `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. |
|
|
320
|
+
| `child-closed` | Read the child completion and remaining open children. Re-scope open work, park obsolete work with `park_child`, and 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). |
|
|
321
|
+
| `design-approved` | Its `version` is the approved one. A human approved the root spec document at its current version; the gate is open. Proceed to section 2. |
|
|
322
|
+
| `design-changes-requested` | Its `version` and `reason`. 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. |
|
|
324
323
|
| `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. |
|
|
325
|
-
| `pr-
|
|
326
|
-
| `pr-
|
|
327
|
-
| `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. |
|
|
324
|
+
| `pr-blocked` | Its `reason` names the pull request and the `max_fix_attempts` it reached. The count is of 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. |
|
|
325
|
+
| `pr-merged` | Its `reason` names the pull request. 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. |
|
|
328
326
|
| `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. |
|
|
329
|
-
|
|
|
330
|
-
| `
|
|
331
|
-
| `
|
|
332
|
-
| `
|
|
327
|
+
| A reply on an ask you follow | Interpret the reply in the issue's design context. Answer 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. |
|
|
328
|
+
| `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. |
|
|
329
|
+
| `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. |
|
|
330
|
+
| `ready-refused` | Its `version` and `reason`. The merger's READY was refused. A `reason` of `READY refused: approve design version <N> before requesting READY.` (or, with `version` 0, `no design version is approved`) means the design gate is closed: get that version approved (section 1), and the daemon advances the merge and posts the packet the merger sent once it is; nothing else is needed. 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. |
|
|
333
331
|
|
|
334
332
|
## Escalation judgment
|
|
335
333
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: legion-controller
|
|
3
|
-
description: Use when handling Legion controller wakes for root-issue triage, keeping the admission slots full from `todo` issues, the daily report, architect escalation,
|
|
3
|
+
description: Use when handling Legion controller wakes for root-issue triage, keeping the admission slots full from `todo` issues, the daily report, architect escalation, or human interaction.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Legion Controller
|
|
@@ -12,73 +12,63 @@ architect.
|
|
|
12
12
|
|
|
13
13
|
## Start and claim the controller role
|
|
14
14
|
|
|
15
|
-
The Legion extension
|
|
16
|
-
|
|
15
|
+
The Legion extension registers this session with the daemon as the controller and claims
|
|
16
|
+
`legion-<project>-controller` during session startup. Do not handle a wake unless that startup
|
|
17
|
+
succeeded.
|
|
17
18
|
|
|
18
|
-
The
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
opens detached, so a bare `attach` lands on whichever window is current). The operator may attach and
|
|
23
|
-
type into this session at any time. The pane carries no GitHub credential: its GitHub token
|
|
24
|
-
variables are emptied, and both `legion gh -- <args>` and `legion threads resolve` are refused.
|
|
25
|
-
The controller reads Dispatch and applies its controller capability with `legion status <KEY>
|
|
26
|
-
<status>`; it never reads GitHub or merges a pull request.
|
|
19
|
+
The session carries no GitHub credential: its GitHub token variables are emptied, and both
|
|
20
|
+
`legion gh -- <args>` and `legion threads resolve` are refused. The controller reads Dispatch and
|
|
21
|
+
applies its controller capability with `legion status <KEY> <status>`; it never reads GitHub or
|
|
22
|
+
merges a pull request.
|
|
27
23
|
|
|
28
24
|
For an interactive takeover from a hand-started OMP session, start OMP with
|
|
29
25
|
`LEGION_CONTROLLER_SECRET` (or `LEGION_CONTROLLER_SECRET_FILE`, a path to a file holding it),
|
|
30
|
-
`LEGION_DAEMON_URL`, `
|
|
26
|
+
`LEGION_DAEMON_URL`, `LEGION_PROJECT` (the daemon's project), `LEGION_STATE_DIR`, and `LEGION_GRANT_FILE`
|
|
31
27
|
(an absolute path to a file only you can read, under a 0700 directory; the extension writes
|
|
32
28
|
each command's grant there and every `bash` call is blocked without it) in its environment. Do
|
|
33
|
-
not set `LEGION_CONTROLLER=1` — that marker is
|
|
34
|
-
claims at startup
|
|
29
|
+
not set `LEGION_CONTROLLER=1` — that marker is `legion controller start`'s own, and a session
|
|
30
|
+
carrying it claims at startup. Then run:
|
|
35
31
|
|
|
36
32
|
```text
|
|
37
33
|
/legion-claim-controller
|
|
38
34
|
```
|
|
39
35
|
|
|
40
|
-
The command
|
|
41
|
-
|
|
36
|
+
The command checks that `LEGION_PROJECT` is the daemon's project, registers this session with the
|
|
37
|
+
daemon, and claims the Envoy role for it before controller commands can act. From then on this session's
|
|
42
38
|
shell commands are wrapped with a controller grant, but the grant holds no GitHub credential;
|
|
43
39
|
`legion status <KEY> <status>` works through the controller secret in this session's environment
|
|
44
40
|
(`LEGION_CONTROLLER_SECRET` or its `_FILE`), not the grant — if it fails, that is the variable to check.
|
|
45
|
-
The takeover moves the role
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
daemon respawns its own pane the secret rotates, every `bash` call in the takeover session then
|
|
55
|
-
fails with a 403 from the grant mint, and the fix is to start a fresh OMP with the new secret,
|
|
56
|
-
not to retry. And the role does not follow `/new` or `/fork` in a takeover session — without
|
|
41
|
+
The takeover moves the role and the daemon's recorded session id to this session. Never pass a
|
|
42
|
+
secret as a command argument or copy it into a transcript. The Envoy registration heartbeat keeps
|
|
43
|
+
the role afterwards, so `/legion-claim-controller` is the manual override, not a routine step after
|
|
44
|
+
a listener restart.
|
|
45
|
+
|
|
46
|
+
Two limits of a takeover session. It caches the controller secret it started with: the next
|
|
47
|
+
`legion controller start` mints a new secret, every `bash` call in the takeover session then fails
|
|
48
|
+
with a 403 from the grant mint, and the fix is to start a fresh OMP with the new secret, not to
|
|
49
|
+
retry. And the role does not follow `/new` or `/fork` in a takeover session — without
|
|
57
50
|
`LEGION_CONTROLLER=1` the new session is not a Legion session to the extension — so after either
|
|
58
51
|
command run `/legion-claim-controller` again.
|
|
59
52
|
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
The daemon holds nothing for a controller: daemon state and the Dispatch project remain
|
|
54
|
+
authoritative, and the start procedure below reads what happened while no controller ran.
|
|
62
55
|
|
|
63
56
|
### Started by the operator
|
|
64
57
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
pane: the operator ran `legion controller start --config controller.yaml [--daemon-url <url>]` on
|
|
58
|
+
The daemon launches no controller, under either runtime: the operator ran
|
|
59
|
+
`legion controller start --config controller.yaml [--daemon-url <url>]` on
|
|
68
60
|
their own machine, and you are that foreground OMP session. The command fetched a fresh controller
|
|
69
61
|
secret from the daemon with the operator's token, wrote it to a 0600 file under `LEGION_STATE_DIR`
|
|
70
62
|
(`~/.local/state/legion/<project>-controller` by default) beside the `gh` shim and the `legion`
|
|
71
|
-
launcher, and started you with `LEGION_CONTROLLER=1` and the
|
|
72
|
-
|
|
73
|
-
`/legion/v1/claims/register` with the
|
|
63
|
+
launcher, and started you with `LEGION_CONTROLLER=1` and the controller's environment. The
|
|
64
|
+
extension registers on `/legion/v1/claims/register` with the
|
|
74
65
|
secret, claims the role, then subscribes to `notifications.legion.<project>.controller`, where the
|
|
75
|
-
|
|
66
|
+
daemon publishes the rows marked from the Go daemon in the wake routing table. The daemon records
|
|
76
67
|
you as `controllerLocator: {runtime, external: true, sessionId, registeredAt}`, `runtime` being the
|
|
77
|
-
daemon's own (`kubernetes
|
|
78
|
-
|
|
79
|
-
`legion-<project>-controller` and its `last_seen`), not from a pane: keep the session running.
|
|
68
|
+
daemon's own (`kubernetes` or `tmux`). The daemon reads your liveness from the Envoy role registry
|
|
69
|
+
(the holder of `legion-<project>-controller` and its `last_seen`): keep the session running.
|
|
80
70
|
Exiting it leaves the project without a controller until the operator runs the command again —
|
|
81
|
-
the
|
|
71
|
+
the daemon logs `controller not registered; run legion controller start` once per
|
|
82
72
|
boot-timeout interval and launches nothing itself. `legion state` and `legion status <KEY>
|
|
83
73
|
<status>` work here over `LEGION_DAEMON_URL`. A second `legion controller start` replaces you: it
|
|
84
74
|
mints a new secret, so your grants stop working and the role moves to the new session.
|
|
@@ -214,7 +204,7 @@ leans on `External links:`, and the label row is the one that never depends on h
|
|
|
214
204
|
| It has any child | `dispatch_read({ ref: "dispatch://<KEY>/children" })` lists any child, open or `done`. It is an umbrella, and a finished umbrella is still no leaf. That also skips an issue whose only child is done, which is accepted. Its open children are candidates themselves, each in its own place in the order. |
|
|
215
205
|
| An ancestor is Legion's | Follow `Links:` up through each `child_of` parent, reading each one, and skip when any ancestor carries the `legion` label or is recorded under `issues` in `legion state --json`, whatever its status: a Legion tree, running or parked, owns its children. An ancestor's claim or route does not skip the issue: a coordinator holding an umbrella files `todo` leaves for others to pick up, and the claim on the issue itself is what keeps two sessions off the same work. A `child_of` under `Referenced by:` is a child of this issue, not its parent. |
|
|
216
206
|
| Someone is designing it | `Open asks:` lists any ask, a `Spec approval: awaiting …` line shows the spec waits on a human, or `Events:` show an `artifact.version` or an `ask.opened` from the last seven days: a session or a person is shaping it even when nobody claims or routes it. |
|
|
217
|
-
| Legion ran it without the label now on it | `legion state --json` records it under `issues`, whatever its status, or `Events:` show a status write by `session legion-daemon:<PROJECT>`, the daemon's actor on every `legion status` (yours included) and on its own `in_progress` at admission. That covers a root a person took the label off, and one that ran before the daemon required the label and never had it. Name each one you skip for this in your summary. The walk never sends a root Legion already ran back into Legion: a person does that with the label and `todo`, and you do it only when a wake below says to (`worker-died
|
|
207
|
+
| Legion ran it without the label now on it | `legion state --json` records it under `issues`, whatever its status, or `Events:` show a status write by `session legion-daemon:<PROJECT>`, the daemon's actor on every `legion status` (yours included) and on its own `in_progress` at admission. That covers a root a person took the label off, and one that ran before the daemon required the label and never had it. Name each one you skip for this in your summary. The walk never sends a root Legion already ran back into Legion: a person does that with the label and `todo`, and you do it only when a wake below says to (`worker-died`). |
|
|
218
208
|
| A running session or a person claims it | `Claimed by:` names anyone and does not end `· not running`. `· liveness unknown` counts as claimed: the agent registry could not be read, so nothing says the holder stopped. A claim ending `· not running` has lapsed, and the issue is free. |
|
|
219
209
|
| Its route reaches a running session | `Route:` names a route with nothing after it, or with `(held by …)`. `(nobody holds it right now)` and `(that session is not running right now)` reach nobody; `(the Envoy listener did not answer, …)` counts as reaching someone. `Route: none` is free. |
|
|
220
210
|
| A pull request is linked or named | `External links:` lists a pull request (kind `github_pr`, or a URL ending `/pull/<n>`), or a comment or message among `Events:` names one. You cannot read GitHub, so an open, merged, or closed pull request all count. A person who wants Legion on it anyway hands it over themselves: the label, then `todo`. |
|
|
@@ -302,7 +292,7 @@ priority first, then board rank ([Keeping the slots full](#keeping-the-slots-ful
|
|
|
302
292
|
it before handling every other wake.
|
|
303
293
|
- **One wake = one turn.** Handle exactly the wake's implication, then end the turn. Never
|
|
304
294
|
poll, idle-loop, or wait for another event. Two additions, after any direct user message: every
|
|
305
|
-
turn
|
|
295
|
+
turn first rechecks the [trees waiting on a root
|
|
306
296
|
claim](#trees-waiting-on-a-root-claim-go-daemon), and your first turn of each UTC day, whatever
|
|
307
297
|
woke you, also posts the day's report ([Daily report](#daily-report-go-daemon)).
|
|
308
298
|
- **Wakes are advisory.** Before any side effect, verify the current daemon state and the
|
|
@@ -318,21 +308,14 @@ priority first, then board rank ([Keeping the slots full](#keeping-the-slots-ful
|
|
|
318
308
|
|
|
319
309
|
| Wake | Content | Controller action |
|
|
320
310
|
|---|---|---|
|
|
321
|
-
| New issue created in the Dispatch project
|
|
322
|
-
| Backlog eligibility (TypeScript daemon) | slot freed / priority change | Reconsider parked items and move the eligible root to `todo` |
|
|
311
|
+
| New issue created in the Dispatch project, status `triage`: `triage on <KEY>` (payload `{kind: "triage"}`) on the controller topic, for an unrecorded root carrying the `legion` label only ("Issues handed to Legion" above); the start procedure above catches one sent while no controller ran | issue key + triage context (incl. pre-existing children) | Triage: `legion status <KEY> todo` to admit, or set `backlog`/`icebox` to park |
|
|
323
312
|
| `slot-free on <KEY>` from the Go daemon (payload `{kind: "slot-free"}`) | the root whose slot the daemon released with no waiting root to take it | Verify a free slot in `legion state --json`, then fill it ([Keeping the slots full](#keeping-the-slots-full-go-daemon)) |
|
|
324
313
|
| `todo on <KEY>` from the Go daemon (payload `{kind: "todo"}`) | an issue not handed to Legion that changed while in `todo` and a slot stood free, sent half a minute later | Verify a free slot, then walk the whole `todo` list ([Keeping the slots full](#keeping-the-slots-full-go-daemon)) |
|
|
325
314
|
| `tick on <PROJECT>` from the Go daemon (payload `{kind: "tick"}`) | the project key; the daemon's periodic wake, whatever the slots | Recheck the trees waiting on a claim, then walk if a slot is free; post the day's report if this is the day's first turn |
|
|
326
315
|
| Architect escalation (controller-actionable only: re-file a child as a root issue, capacity, cross-tree conflicts) | request + context | Judge and act; the owning architect writes an issue-design decision as a decision block and opens `dispatch_ask` only for a human to-do |
|
|
327
|
-
| Resync report | artifact-driven anomaly list (zero-owner trees, untriaged-open, launch-failed, admission-drift) | Verify against fresh state, then heal |
|
|
328
|
-
| Resync report: `admission-drift` entry | issue key + whether the daemon added it to, or removed it from, its admission list (the detail says which) | No action: the daemon already repaired it in the same run. An issue that reappears in consecutive reports is a live leak — file a LEGION issue on Dispatch with both reports pasted as evidence (never a GitHub issue) |
|
|
329
|
-
| `child-status` | child key + status transition | Not controller-actionable by default; if the daemon could not route it to the parent's architect role, verify the transition and forward it with `envoy_publish` |
|
|
330
316
|
| Mention | Slack/GitHub PR @mention text | Answer, or route to the owning issue's architect role |
|
|
331
|
-
| READY packet seen on a Dispatch issue (via issue subscription) | READY line + gate facts | No action: a human merges; the merger has already notified the queue role if the project has one |
|
|
332
|
-
| `worker-recovered` (role `architect`) from the daemon | issue, fromRef | A root architect's tree volume was lost; it restarted as a new session. Verify the tree is active in `legion state` and that the architect posts its next step on the issue within one resync interval; otherwise treat it as an anomaly. |
|
|
333
317
|
| `held on <KEY>` from the Go daemon (payload `{kind: "held", phase, role?, reason?}`) | the held issue, the phase it left, and the role whose claim failed, or `reason: "escalated"` | Verify the hold in `legion state` (the issue's phase is `held`). Without `reason`, a phase worker's launches or prompts ran out and the tree's architect decides retry or escalate: no action. With `reason: "escalated"` (on the record, `issues.<KEY>.holdReason` is `escalated`), the architect sent it to you: handle it as an architect escalation below. Parking the tree is `legion status <root> backlog`; setting the root back to `todo` later re-admits it as a new generation, which starts again from its architect |
|
|
334
318
|
| `worker-died on <KEY>` from the Go daemon with `role: "architect"` | the tree root whose architect's claim failed, and the phase the root was in | The tree's architect ran out of launches or prompts and the daemon relaunches nothing; every other notice of the tree goes to that architect, so nobody inside the tree can act. Verify in `legion state` (`issues.<KEY>.architect.state` is `failed`); if the root's phase is `done`, the tree is already parked: no action. Otherwise re-admit the tree (`legion status <root> backlog`, then `todo`: a new generation, whose architect starts again with fresh budgets) or leave it parked and say why on the issue |
|
|
335
|
-
| Closed-tree activity (comment, review, CI on a closed tree) | issue, root, event summary | Read the artifact; if work should resume, `legion status <root> todo`; otherwise no action — the event is not held or redelivered |
|
|
336
319
|
| Direct user message | — | Always first |
|
|
337
320
|
|
|
338
321
|
## New issue triage
|
|
@@ -357,8 +340,8 @@ priority first, then board rank ([Keeping the slots full](#keeping-the-slots-ful
|
|
|
357
340
|
```
|
|
358
341
|
|
|
359
342
|
(or `icebox` for longer-term deferral). Dispatch status is the durable record; there is no
|
|
360
|
-
other marker to maintain, beyond the
|
|
361
|
-
place.
|
|
343
|
+
other marker to maintain, beyond the `legion` label, which parking leaves in
|
|
344
|
+
place. A root never waits for capacity in `backlog`: in `todo` the
|
|
362
345
|
daemon's admission queue holds it (`admission.waiting`), so park only a root that should not
|
|
363
346
|
run now. Do not triage a system-created child as a root issue.
|
|
364
347
|
4. When you post a triage note (a `dispatch_comment` on the issue saying what you decided and
|
|
@@ -374,18 +357,13 @@ priority first, then board rank ([Keeping the slots full](#keeping-the-slots-ful
|
|
|
374
357
|
|
|
375
358
|
## Backlog eligibility
|
|
376
359
|
|
|
377
|
-
|
|
360
|
+
Nothing reconsiders `backlog` or `icebox` on its own: [Keeping the slots
|
|
378
361
|
full](#keeping-the-slots-full-go-daemon) takes only `todo` issues, since an issue that waits on a
|
|
379
362
|
deploy or a decision belongs in `backlog` (`skill://dispatch`, "Choosing what to work on"). A
|
|
380
363
|
handed-over root waits for a slot in `todo`, where the daemon's
|
|
381
364
|
admission queue holds it (`admission.waiting`), so a park means "should not run now", and a
|
|
382
365
|
parked root keeps its `legion` label. A parked issue runs again when a person sets it to `todo`,
|
|
383
|
-
or when a wake tells you to re-admit it (`worker-died
|
|
384
|
-
|
|
385
|
-
Under the TypeScript daemon, when a slot frees or priority changes, use `legion state --json` and
|
|
386
|
-
the current Dispatch issue to reconsider parked roots. Admit the selected root with
|
|
387
|
-
`legion status <KEY> todo`. Moving an item to or from `backlog`/`icebox` is a deliberate
|
|
388
|
-
controller decision, not a no-op.
|
|
366
|
+
or when a wake tells you to re-admit it (`worker-died`).
|
|
389
367
|
|
|
390
368
|
## Architect escalation
|
|
391
369
|
|
|
@@ -397,7 +375,7 @@ For an independence judgment, verify the child and its parent against current da
|
|
|
397
375
|
and the Dispatch issue. If the work belongs in an independent root:
|
|
398
376
|
|
|
399
377
|
1. File a **fresh root issue** with `dispatch_issue({ project, title, spec })` (no `parent`;
|
|
400
|
-
|
|
378
|
+
add `labels: ["legion"]`, without which it is never admitted).
|
|
401
379
|
`project` is the issue key's prefix before `-<n>` (e.g. `LEGSMOKE-3` → `LEGSMOKE`): the
|
|
402
380
|
project key exactly as `legion.yaml` writes it, which `legion state --json` shows as
|
|
403
381
|
`daemon.project`. It is not the lowercase project token in role names such as
|
|
@@ -411,17 +389,6 @@ Never promote a child in place. Resolve capacity and cross-tree conflicts from v
|
|
|
411
389
|
state, routing design decisions back to the owning architect when they are not controller
|
|
412
390
|
judgments.
|
|
413
391
|
|
|
414
|
-
## Resync report
|
|
415
|
-
|
|
416
|
-
Treat a resync report as an anomaly list, not an instruction. For every zero-owner tree,
|
|
417
|
-
untriaged-open, or launch-failed issue it names, verify `legion state --json` and the
|
|
418
|
-
current Dispatch issue first. Then heal the verified condition: admit an eligible root, move
|
|
419
|
-
an issue back to its intended status, or use the applicable daemon control path. Do not act
|
|
420
|
-
on stale entries until their source artifact explains the anomaly. An `admission-drift` entry
|
|
421
|
-
needs no healing — the daemon added the tree back to (or removed it from) its admission list in
|
|
422
|
-
the same run; verify only that the same issue does not recur in the next report, and file a
|
|
423
|
-
LEGION issue with both reports if it does.
|
|
424
|
-
|
|
425
392
|
## Mentions
|
|
426
393
|
|
|
427
394
|
Read the mention and its artifact. Answer it when it asks the controller for triage or
|
|
@@ -5,9 +5,9 @@ description: Use when an issue has passed review and its parked implementer is r
|
|
|
5
5
|
|
|
6
6
|
# Legion Retro
|
|
7
7
|
|
|
8
|
-
Retro is mandatory for every issue that passed review. The
|
|
9
|
-
|
|
10
|
-
skill obtains a separate fresh-eyes perspective. Retro runs before merge.
|
|
8
|
+
Retro is mandatory for every issue that passed review. The daemon starts the implementer on it
|
|
9
|
+
once the reviewer approves, so the person with implementation context performs the retrospective,
|
|
10
|
+
and the skill obtains a separate fresh-eyes perspective. Retro runs before merge.
|
|
11
11
|
|
|
12
12
|
Every path this skill cites (`packages/...`, `docs/...`) is in sjawhar/legion, the Legion
|
|
13
13
|
repository, which need not be the repository you are working in.
|
|
@@ -18,16 +18,17 @@ Follow this ordering exactly. It keeps the reviewed branch clean while preservin
|
|
|
18
18
|
retrospective's durable output.
|
|
19
19
|
|
|
20
20
|
1. Tester green and all code-review cycles finish.
|
|
21
|
-
2. The
|
|
22
|
-
|
|
21
|
+
2. The reviewer approves the head. It still carries `.legion/`: no role removes it before the
|
|
22
|
+
merge, and the operator removes it from the default branch after the merge.
|
|
23
23
|
3. Run this retro: commit durable learnings to `docs/solutions/` and post the retro message on
|
|
24
24
|
the Dispatch issue.
|
|
25
|
-
Retro writes **no `.legion` file**, so it never
|
|
25
|
+
Retro writes **no `.legion` file**, so it never changes the approved head's handoffs.
|
|
26
26
|
4. The merger verifies the tip is the approved head plus commits that change only
|
|
27
27
|
`docs/solutions/` — `jj diff --from <approved-sha> --to <tip-sha> --summary`, quoted in READY —
|
|
28
|
-
|
|
29
|
-
role when configured. A human merges under the
|
|
30
|
-
CODEOWNERS requirements; GitHub's merge queue
|
|
28
|
+
and sends the READY packet with its completion; the daemon posts it on the Dispatch issue and
|
|
29
|
+
publishes it to the project's merge-queue role when one is configured. A human merges under the
|
|
30
|
+
repository's GitHub branch-protection and CODEOWNERS requirements; GitHub's merge queue
|
|
31
|
+
participates only when the repository enables it.
|
|
31
32
|
5. After that merge, the implementer — not the reviewer or merger — verifies the change in production
|
|
32
33
|
and records it on the PR and the issue: the agent that developed it is responsible for testing
|
|
33
34
|
in production. The architect's sign-off waits for that record.
|
|
@@ -41,14 +42,14 @@ changes only `docs/solutions/` does not void it, and the tree goes from retro to
|
|
|
41
42
|
never back to the tester or reviewer. A conflict-forced rebase after retro moves these documents
|
|
42
43
|
with the branch; retro does not re-run.
|
|
43
44
|
|
|
44
|
-
Do not start retro before step 2
|
|
45
|
-
|
|
45
|
+
Do not start retro before step 2 or skip it because the change seems mechanical; the merger's
|
|
46
|
+
`READY` comes only after step 3. The design gate is not a substitute for review and retro.
|
|
46
47
|
|
|
47
48
|
## Two perspectives
|
|
48
49
|
|
|
49
50
|
1. Re-read the issue, its acceptance criteria, the PR, test evidence, and review evidence.
|
|
50
51
|
Confirm the PR carries both proofs: the implementer's own `E2E (implementer)` line and the tester's `E2E (tester)` line,
|
|
51
|
-
each naming a production-like surface (the daemon's
|
|
52
|
+
each naming a production-like surface (the daemon's real-process tests and the live proofs under `scripts/e2e/`; a live check at the operator's next daemon restart, recorded on the PR; a sandbox repository, a devN
|
|
52
53
|
stack, staging, or a local stack with real migrations), a command or run id, an observation, a head
|
|
53
54
|
SHA, and a negative control. If either is missing, or links only a unit suite, the retro's first
|
|
54
55
|
durable learning is that gap and the issue goes back — to the implementer for its own proof, to the
|
|
@@ -87,8 +88,8 @@ related_issues:
|
|
|
87
88
|
---
|
|
88
89
|
```
|
|
89
90
|
|
|
90
|
-
Commit the documentation on the existing issue branch
|
|
91
|
-
|
|
91
|
+
Commit the documentation on the existing issue branch and push it with `legion push`. Do not
|
|
92
|
+
create a replacement branch or bookmark. Then post one Dispatch message on
|
|
92
93
|
the issue — `issue` is your `LEGION_ISSUE`; Legion issues live on Dispatch, never on a GitHub
|
|
93
94
|
issue, and the `gh` shim refuses every GitHub-issue write — naming the documents, the
|
|
94
95
|
one-to-three most useful takeaways, the two proofs you read, and the production check that
|
|
@@ -115,16 +116,16 @@ dispatch_message({
|
|
|
115
116
|
```
|
|
116
117
|
|
|
117
118
|
The Dispatch message and the `docs/solutions/` commit are the only retro outputs. Never write a
|
|
118
|
-
handoff, phase artifact, local feedback log, or completion label
|
|
119
|
-
|
|
119
|
+
handoff, phase artifact, local feedback log, or completion label, and add or change nothing under
|
|
120
|
+
`.legion/`. Report completion with the `legion` tool's `handoff_complete` alone (its
|
|
120
121
|
summary: two sentences for the architect) — no `handoff_write`.
|
|
121
122
|
|
|
122
123
|
## Completion check
|
|
123
124
|
|
|
124
125
|
Before returning, verify all of the following:
|
|
125
126
|
|
|
126
|
-
- The reviewer
|
|
127
|
-
approval of that
|
|
127
|
+
- The reviewer-approved head remains below the retro documentation commit, and the reviewer's
|
|
128
|
+
approval of that head stands: the merger accepts the approved head plus this commit.
|
|
128
129
|
- The learning documents and the Dispatch message both exist (never a GitHub issue comment).
|
|
129
130
|
- Both proofs were read, and any gap in either is recorded as a learning.
|
|
130
131
|
- No `.legion` file was created or modified by retro.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: legion-worker
|
|
3
|
-
description: Use when dispatched as a per-process Legion phase worker —
|
|
3
|
+
description: Use when dispatched as a per-process Legion phase worker — planner, implementer, tester, reviewer, or merger — booted from the daemon's LEGION_* environment.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Legion Phase Worker
|
|
@@ -24,26 +24,26 @@ with the `read` tool: a read by filesystem path stops at 300 lines.
|
|
|
24
24
|
| You write or edit the PR body, record a proof (an `E2E` line or a handoff `proof` array), verify another phase's proof, or run the simplify pass | `skill://legion-worker/references/pr-body.md` |
|
|
25
25
|
| You reply to, accept, or resolve a review thread, or run `legion threads resolve` | `skill://legion-worker/references/review-threads.md` |
|
|
26
26
|
| GitHub reports a conflict, the pull request is retargeted, you compare heads after a conflict merge, or you would rewrite a pushed commit | `skill://legion-worker/references/conflicts-and-rewrites.md` |
|
|
27
|
-
| You review or approve,
|
|
27
|
+
| You review or approve, build the READY packet, or check the merge in production | `skill://legion-worker/references/merge-gate.md` |
|
|
28
28
|
| The issue renames a repository, package, or URL across the codebase | `skill://legion-worker/references/systematic-rename.md` |
|
|
29
29
|
| The issue deletes code, a command, or documentation | `skill://legion-worker/references/cleanup-deletion.md` |
|
|
30
30
|
|
|
31
31
|
## Identity, scope, and role
|
|
32
32
|
|
|
33
33
|
The daemon spawns you as a separate `omp --mode rpc` process (behind `legion worker-shim`,
|
|
34
|
-
in a tmux pane) with `LEGION_TREE`, `LEGION_ISSUE`, `LEGION_ROLE`,
|
|
35
|
-
`LEGION_BOOT_TOKEN_FILE` (
|
|
36
|
-
the extension reads it for you), `LEGION_DAEMON_URL`, `LEGION_STATE_DIR`, and
|
|
37
|
-
in your environment
|
|
34
|
+
in a tmux pane or an Agent Sandbox pod) with `LEGION_TREE`, `LEGION_ISSUE`, `LEGION_ROLE`,
|
|
35
|
+
`LEGION_GENERATION`, `LEGION_PROJECT`, `LEGION_BOOT_TOKEN_FILE` (the file holding your boot
|
|
36
|
+
token; the extension reads it for you), `LEGION_DAEMON_URL`, `LEGION_STATE_DIR`, and
|
|
37
|
+
`LEGION_WORKSPACE` in your environment. The extension
|
|
38
38
|
completes the boot handshake for you at session start — it registers with the daemon, claims
|
|
39
39
|
your role, and signals readiness. You never call `envoy_role_set` yourself.
|
|
40
40
|
|
|
41
41
|
Your role token is not the issue key spelled out literally. The daemon encodes it as
|
|
42
42
|
`legion-<project>-<key>-<role>` with the issue key lower-cased. For example, project `acme`, issue
|
|
43
43
|
`LEGION-41`, role `architect` encodes to `legion-acme-legion-41-architect`. Never hand-format one for another role: your own role
|
|
44
|
-
topic and the topic of the architect that owns your issue are
|
|
45
|
-
prompt (
|
|
46
|
-
|
|
44
|
+
topic and the topic of the architect that owns your issue are in the `Legion addressing` line of
|
|
45
|
+
your system prompt (the daemon appends it; it names the tree root's architect, also on a child
|
|
46
|
+
issue), a sibling role's topic is yours with the
|
|
47
47
|
trailing `-<role>` replaced, and the `roleToken` helper in `@legion/contracts` computes any other
|
|
48
48
|
one exactly the way the daemon does — prefer a topic you've already been given before recomputing
|
|
49
49
|
one.
|
|
@@ -166,7 +166,7 @@ other tree paused.
|
|
|
166
166
|
|
|
167
167
|
## Phase work
|
|
168
168
|
|
|
169
|
-
Specifications written into Dispatch follow `skill://dispatch`'s
|
|
169
|
+
Specifications written into Dispatch follow `skill://dispatch`'s "Writing a spec" section, except that a phase worker writes no decision block: it sends an open product, scope or design decision to its architect, which writes the block.
|
|
170
170
|
|
|
171
171
|
Follow the repository's normal engineering workflow and the assigned issue's acceptance
|
|
172
172
|
criteria. Your phase's own charter and the predecessor handoffs you read define the phase
|
|
@@ -214,7 +214,7 @@ legion gh -- <gh args…>
|
|
|
214
214
|
```
|
|
215
215
|
|
|
216
216
|
Four facts about `gh` in a worker pane. The `gh` on your `PATH` is a shim
|
|
217
|
-
(`<state_dir>/worker-bin/gh`, installed by the daemon at startup — `packages/daemon/
|
|
217
|
+
(`<state_dir>/worker-bin/gh`, installed by the daemon at startup — `packages/daemon/internal/runtime/workerbin/workerbin.go`)
|
|
218
218
|
that execs `legion gh -- "$@"`, so `gh …` and `legion gh -- …` are the same call, and each call
|
|
219
219
|
redeems a fresh token from your session's grant — identity is supplied per call, never stored.
|
|
220
220
|
Never run `gh auth login` or `gh auth setup-git`; there is no login state to create. The shim
|
|
@@ -282,8 +282,8 @@ pushes the issue branch under this exact name with the one push procedure every
|
|
|
282
282
|
(*Every role pushes its own commits*, below).
|
|
283
283
|
|
|
284
284
|
The provisioned issue workspace configures `credential.helper` with the daemon's absolute
|
|
285
|
-
credential command, so `
|
|
286
|
-
|
|
285
|
+
credential command, so `legion push` authenticates transparently through the same session
|
|
286
|
+
capability. Never handle a token.
|
|
287
287
|
|
|
288
288
|
Then open the pull request with `legion gh -- pr create`. The PR body **must** contain the
|
|
289
289
|
line `Dispatch: <KEY>` — the daemon's fallback link from a PR to its Dispatch issue when the
|
|
@@ -314,9 +314,9 @@ line), the full definition of a proof, what the tester verifies, and the simplif
|
|
|
314
314
|
- **A conflict or a retarget** is the only reason to bring the base into the branch, always as a
|
|
315
315
|
forward merge and never `jj rebase`; the unchanged-diff fingerprint each role compares
|
|
316
316
|
afterwards is in the same reference: `skill://legion-worker/references/conflicts-and-rewrites.md`.
|
|
317
|
-
- **The merge gate**, in order: the tester's evidence green → the
|
|
318
|
-
|
|
319
|
-
|
|
317
|
+
- **The merge gate**, in order: the tester's evidence green → the reviewer's approval of the head
|
|
318
|
+
→ retro → the merger's READY → the human merge → the implementer's production check. Legion
|
|
319
|
+
never merges. The reviewer's submissions,
|
|
320
320
|
retro's commit, the merger's READY, and the production check follow
|
|
321
321
|
`skill://legion-worker/references/merge-gate.md`.
|
|
322
322
|
- **No surface reaches the changed path** is a report to the architect, never a reason to
|
|
@@ -359,58 +359,25 @@ cd -- "$LEGION_WORKSPACE" && \
|
|
|
359
359
|
```
|
|
360
360
|
|
|
361
361
|
**Every role pushes its own commits.** After the handoff commit — and, for the tester, the red
|
|
362
|
-
tests it wrote —
|
|
363
|
-
which authenticates as your role's App (`appRoleForLegionRole` in
|
|
364
|
-
`packages/daemon/src/daemon/github-apps.ts`).
|
|
365
|
-
|
|
366
|
-
**Under the Go daemon, every push is `legion push`,** run from bash in your workspace in place of
|
|
367
|
-
the commands below. It runs this same procedure on `@-`: the ancestry check, against the remote
|
|
368
|
-
branch or the tip you recorded before rewriting pushed commits (below), then the bookmark and the
|
|
369
|
-
push. It also decides whether the push skips CI. A push skips CI only when none of its commits
|
|
370
|
-
touches anything but handoffs whose phase guarantees a later push: the planner's
|
|
371
|
-
`.legion/plan.json`, the tester's `.legion/test.json`, and a reviewer's `.legion/review.json` whose
|
|
372
|
-
`verdict` is `"changes_requested"`. Its head then ends with GitHub's `skip-checks: true` trailer,
|
|
373
|
-
and the Go daemon carries the code head's verdict to it. Every other push runs CI in full. Never
|
|
374
|
-
add or remove that trailer yourself, never write one of GitHub's bracket keywords (`[skip ci]`,
|
|
375
|
-
`[ci skip]`, `[no ci]`, `[skip actions]`, `[actions skip]`) into a commit message, and never push
|
|
376
|
-
the issue branch with the commands below under the Go daemon: a hand-run push of a handoff that
|
|
377
|
-
could skip CI runs it in full, and a hand-added trailer or keyword on any other push skips CI on a
|
|
378
|
-
head a human may merge. `legion push` refuses a head whose message carries a keyword and pushes
|
|
379
|
-
nothing until you take it out. Under the TypeScript daemon, whose `legion` has no `push` command,
|
|
380
|
-
run the commands below yourself.
|
|
381
|
-
|
|
382
|
-
`-r @-` puts the bookmark on the commit you just
|
|
383
|
-
split off: the working copy left above it has no description, and `jj git push` refuses a
|
|
384
|
-
commit without one. `--allow-backwards` is for that local step alone: after a split the bookmark
|
|
385
|
-
can sit on the undescribed working copy above `@-`. `--bookmark` also publishes the locally
|
|
386
|
-
provisioned bookmark on its first push — a bookmark not yet tracking a remote one is tracked
|
|
387
|
-
automatically. This is the one push procedure, run as `legion push` under the Go daemon and by
|
|
388
|
-
hand under the TypeScript daemon; every push of the issue branch uses it:
|
|
362
|
+
tests it wrote — push the issue branch with `legion push`, run from bash in your workspace:
|
|
389
363
|
|
|
390
364
|
```bash
|
|
391
|
-
cd -- "$LEGION_WORKSPACE" &&
|
|
392
|
-
tip_file="${TMPDIR:-/tmp}/legion-<KEY>-$LEGION_ROLE-rewritten-tip" && \
|
|
393
|
-
old=$(cat -- "$tip_file" 2>/dev/null || true) && \
|
|
394
|
-
behind=$(jj -R "$LEGION_WORKSPACE" log --no-graph -T 'commit_id.short() ++ "\n"' \
|
|
395
|
-
-r "remote_bookmarks(exact:\"legion/<KEY>\", exact:\"origin\") ~ (::@-${old:+ | $old})") && \
|
|
396
|
-
{ [ -z "$behind" ] || { echo "legion/<KEY>@origin is at $behind, which @- does not descend from" >&2; false; }; } && \
|
|
397
|
-
jj -R "$LEGION_WORKSPACE" bookmark set legion/<KEY> -r @- --allow-backwards && \
|
|
398
|
-
jj -R "$LEGION_WORKSPACE" git push --bookmark legion/<KEY> && \
|
|
399
|
-
rm -f -- "$tip_file"
|
|
365
|
+
cd -- "$LEGION_WORKSPACE" && legion push
|
|
400
366
|
```
|
|
401
367
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
push
|
|
368
|
+
It pushes `@-` through the provisioned credential helper, which authenticates as your role's App
|
|
369
|
+
(`appauth.AppRoleFor` in `packages/daemon/internal/appauth/identity.go`). Your system prompt says
|
|
370
|
+
which pushes skip CI and which commit-message keywords it refuses. Its ancestry check refuses
|
|
371
|
+
unless `@-` descends from `legion/<KEY>@origin` (or the branch is not on GitHub yet): every issue
|
|
372
|
+
workspace shares one clone, so another role's push moves `legion/<KEY>@origin` here at once, and
|
|
373
|
+
a push that did not descend from it would move the remote branch sideways onto your commit and
|
|
374
|
+
drop theirs.
|
|
408
375
|
|
|
409
376
|
Before any rewrite of a commit you already pushed — a `jj squash --into` one, or any other
|
|
410
377
|
rewrite — read *Rewriting pushed commits* in
|
|
411
378
|
`skill://legion-worker/references/conflicts-and-rewrites.md`: it checks that no other tree's work
|
|
412
|
-
is built on that commit and records the pushed tip
|
|
413
|
-
|
|
379
|
+
is built on that commit and records the pushed tip `legion push` reads, the only case in which a
|
|
380
|
+
push lets the remote branch move off its current tip.
|
|
414
381
|
|
|
415
382
|
Before the push, check ancestry and identity as above: the chain carries every earlier phase's
|
|
416
383
|
commits, and pushing them with yours is expected. A refusal, and a push the remote rejects, is a
|
|
@@ -418,14 +385,10 @@ report to the architect with the output, never a force-push. The merger makes no
|
|
|
418
385
|
pushes nothing.
|
|
419
386
|
|
|
420
387
|
Do not report phase completion until the write, existence check, handoff commit, and push
|
|
421
|
-
succeed. This is the committed copy the next phase
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
stderr), this gate no longer applies: a later rebase, bare-gate re-check, confirmation, retro, or
|
|
426
|
-
the post-merge production check writes no `.legion/<phase>.json`, commits no handoff, and reports
|
|
427
|
-
with `handoff_complete` alone (below). Recreating `.legion/` after its deletion changes the
|
|
428
|
-
approved head and restarts the review loop this rule exists to end.
|
|
388
|
+
succeed. This is the committed copy the next phase reads after revival. No phase removes
|
|
389
|
+
`.legion/`: the reviewer approves a head that carries it, and the operator removes it from the
|
|
390
|
+
default branch after the merge. Retro and the post-merge production check write no
|
|
391
|
+
`.legion/<phase>.json`, commit no handoff, and report with `handoff_complete` alone (below).
|
|
429
392
|
|
|
430
393
|
## Completion: report to the architect, then stay
|
|
431
394
|
|
|
@@ -1,35 +1,32 @@
|
|
|
1
1
|
# The merge gate: review, retro, READY, and the production check
|
|
2
2
|
|
|
3
3
|
Part of `skill://legion-worker`. Read it when you are the reviewer submitting a review or an
|
|
4
|
-
approval, the implementer
|
|
5
|
-
|
|
4
|
+
approval, the implementer recording the production check, or the merger building READY. Every
|
|
5
|
+
path it cites is in sjawhar/legion.
|
|
6
6
|
|
|
7
|
-
The order, in full: the tester's evidence green → the
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
The order, in full: the tester's evidence green → the reviewer's approval of the head →
|
|
8
|
+
retro → the merger's READY → the human merge → the implementer's production check. No role
|
|
9
|
+
removes `.legion/` before the merge: the approved head carries it, and the operator removes it
|
|
10
|
+
from the default branch after the merge. After the approval, only retro's `docs/solutions/`
|
|
11
|
+
commit leaves it standing on its own (*Retro*, below). A conflict-forced merge goes back to the
|
|
12
|
+
reviewer for a confirmation or a new round, as the fingerprint decides (*The reviewer*, below,
|
|
13
|
+
and `skill://legion-worker/references/conflicts-and-rewrites.md`), and any other change voids it.
|
|
13
14
|
|
|
14
15
|
## The reviewer
|
|
15
16
|
|
|
16
17
|
- The reviewer verifies the `CI`, `Threads`, and `E2E` facts against GitHub directly —
|
|
17
18
|
never from a handoff — then runs `task(agent="thermonuclear-deep-review")` and
|
|
18
|
-
`task(agent="thermonuclear-code-quality")` once at that head — the head the
|
|
19
|
-
|
|
19
|
+
`task(agent="thermonuclear-code-quality")` once at that head — the head the round reviews —
|
|
20
|
+
and records the verdict.
|
|
20
21
|
Approval is refused while either `E2E (implementer)` or `E2E (tester)` is missing: `REQUEST_CHANGES` naming the missing line.
|
|
21
22
|
Skip the `Thermo` line entirely on a docs-only PR. Submit **one review per round** —
|
|
22
|
-
`REQUEST_CHANGES` when any correctness finding stands, otherwise `
|
|
23
|
-
|
|
24
|
-
that differs from the reviewed one by the `.legion/` deletion alone, or, after a
|
|
25
|
-
conflict-forced rebase, the new head whose fingerprint equals the approved head's — always
|
|
26
|
-
named by SHA — carrying every inline comment in that single
|
|
23
|
+
`REQUEST_CHANGES` when any correctness finding stands, otherwise `APPROVE` of the head you
|
|
24
|
+
reviewed — always named by SHA — carrying every inline comment in that single
|
|
27
25
|
call: `legion gh -- api --method POST repos/{owner}/{repo}/pulls/{number}/reviews --input body.json`
|
|
28
|
-
with `commit_id`, `event` (`REQUEST_CHANGES
|
|
26
|
+
with `commit_id`, `event` (`REQUEST_CHANGES` or `APPROVE`), `body` (with the
|
|
29
27
|
Legion footer), and a `comments[]` array of `{path, line, side, body}`, one entry per
|
|
30
28
|
finding — never one `pr review` call per finding (each submission fires a `pr-review` wake).
|
|
31
|
-
|
|
32
|
-
back to push the `.legion/` deletion, then review **that** head and approve it by name. After a
|
|
29
|
+
A `COMMENT` decides nothing. After a
|
|
33
30
|
conflict-forced rebase, compute the fingerprint (*The unchanged-diff check* in
|
|
34
31
|
`skill://legion-worker/references/conflicts-and-rewrites.md`) at the
|
|
35
32
|
`commit_id` of your last submitted review and at the new head. Equal and that review was
|
|
@@ -41,8 +38,8 @@ confirmation or a new round, as the fingerprint decides (*The reviewer*, below,
|
|
|
41
38
|
Apps, as `skill://legion-worker/references/review-threads.md` says; the same reference says
|
|
42
39
|
when every thread is settled enough to approve, and resolving one never gates your approval.
|
|
43
40
|
|
|
44
|
-
A reviewer's phase ends with its completion, not with its review.
|
|
45
|
-
takes this order: write, commit and push the handoff; submit the review of the head that push
|
|
41
|
+
A reviewer's phase ends with its completion, not with its review. Every round writes a handoff
|
|
42
|
+
and takes this order: write, commit and push the handoff; submit the review of the head that push
|
|
46
43
|
made, by its SHA; then complete. An approval waits for the CI verdict to settle green at that head
|
|
47
44
|
before you submit it, since an approval stands only on green checks and GitHub can dismiss one
|
|
48
45
|
once the head moves, and a verdict that settles red there makes the round's decision a request for
|
|
@@ -57,40 +54,39 @@ require. A required check that was cancelled, or that the head's checks
|
|
|
57
54
|
settled without, leaves no verdict until a later settlement decides it, since a run can be
|
|
58
55
|
cancelled or not yet queued when the head settles; a required workflow's run on the head that is
|
|
59
56
|
still going or has not happened leaves none either. A review of a head the handoff push
|
|
60
|
-
then replaces names a head the pull request no longer has.
|
|
61
|
-
approval of the `.legion/` deletion head) reviews the head as it is. The daemon moves the issue
|
|
57
|
+
then replaces names a head the pull request no longer has. The daemon moves the issue
|
|
62
58
|
once both are in — the decision GitHub reports and your completion, in either order — so a
|
|
63
59
|
review posted without a completion leaves the issue in reviewing until you finish.
|
|
64
60
|
|
|
65
61
|
## Retro
|
|
66
62
|
|
|
67
63
|
- **Retro's commit does not void the reviewer's approval.** After the reviewer approves the
|
|
68
|
-
|
|
64
|
+
head, retro commits its learnings under `docs/solutions/` on top of it; that commit
|
|
69
65
|
stays, the approval stands, and the tree goes to the merger — never back to the tester or
|
|
70
66
|
reviewer. Anything else above the approved head does void it, and the merger tells the
|
|
71
|
-
architect the head must return to review instead of
|
|
67
|
+
architect the head must return to review instead of completing. A conflict-forced rebase
|
|
72
68
|
after retro moves those documents with the branch; retro never re-runs.
|
|
73
69
|
|
|
74
70
|
## The merger
|
|
75
71
|
|
|
76
72
|
- The merger runs `legion threads resolve --pr <n> --repo <owner>/<repo>` (it acts as the same
|
|
77
73
|
code-writing App as the implementer; resolving a thread changes no commit, so this run never
|
|
78
|
-
invalidates the approval), does not
|
|
74
|
+
invalidates the approval), does not complete while any `left open` line remains or the command
|
|
79
75
|
exits 1 (report the thread to the architect instead), then proves that rule with two commands.
|
|
80
76
|
First `cd -- "$LEGION_WORKSPACE" && jj -R "$LEGION_WORKSPACE" git fetch && jj -R
|
|
81
77
|
"$LEGION_WORKSPACE" diff --from <approved-sha> --to <tip-sha> --summary`, whose output is quoted
|
|
82
78
|
in READY (an empty output is quoted as `no file changes above the approved head`); then the same
|
|
83
|
-
with `'~docs/solutions'` appended, which must print nothing. *The READY packet
|
|
84
|
-
|
|
79
|
+
with `'~docs/solutions'` appended, which must print nothing. *The READY packet* is
|
|
80
|
+
`READY #<n> at <current sha> (approved at <approved sha>) for <KEY> (<pr url>)`
|
|
85
81
|
(the shape `packages/daemon/internal/prompts/roles/merger.md` defines), then the PR body's
|
|
86
82
|
`Outcome:` line and its `Not proven / risk:` value — every bullet under that label joined with
|
|
87
83
|
`; ` on the one READY line, or `none` — quoted from the `## For the reviewer` block at that same
|
|
88
|
-
head (or one line saying the body carries no brief — the packet still
|
|
89
|
-
`--summary` output and the PR body's gate facts
|
|
90
|
-
`
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
one is reported to the architect instead of
|
|
84
|
+
head (or one line saying the body carries no brief — the packet still goes out), then the
|
|
85
|
+
`--summary` output and the PR body's gate facts. The merger sends it as the `summary` of its
|
|
86
|
+
`handoff_complete` with `ready: true`; the daemon posts it as a `dispatch_message` on the issue,
|
|
87
|
+
publishes it to the project's merge queue role when one is set, and says on the issue when that
|
|
88
|
+
role has no live holder. The READY packet names both the implementer's and tester's `E2E` lines;
|
|
89
|
+
a missing one is reported to the architect instead of completing. Legion never merges.
|
|
94
90
|
|
|
95
91
|
## After the human merge
|
|
96
92
|
|
|
@@ -36,7 +36,7 @@ left open <thread URL> — newest reply by <login> is not an acceptance
|
|
|
36
36
|
left open <thread URL> — newest reply by <login> is an unsubmitted draft in a pending review
|
|
37
37
|
left open <thread URL> — newest reply by <login> is not its opener's or the Legion reviewer's acceptance
|
|
38
38
|
|
|
39
|
-
**Thermo:** `ce-simplify-code` once at <head-sha>: <0 applied | applied → new head <sha>>; thermonuclear pair at the
|
|
39
|
+
**Thermo:** `ce-simplify-code` once at <head-sha>: <0 applied | applied → new head <sha>>; thermonuclear pair at the reviewed head <sha>:
|
|
40
40
|
<verdict>. (omitted entirely on a docs-only PR — there is no code for either pass, so neither runs)
|
|
41
41
|
|
|
42
42
|
**E2E (implementer):** <surface> — ran `<command or run id>`, observed <result>, at head <sha>.
|
|
@@ -66,7 +66,7 @@ repos/{owner}/{repo}/pulls/{number} --jq .body`, edit, then `--method PATCH ...
|
|
|
66
66
|
`Not proven / risk` copies every claim recorded as unproven before READY — the tester's
|
|
67
67
|
`failures`, the reviewer's own review, any proof-check comment already on the pull request —
|
|
68
68
|
word for word, and `none` is a finding while one stands. The merger quotes `Outcome` and
|
|
69
|
-
`Not proven / risk` from the body at the
|
|
69
|
+
`Not proven / risk` from the body at the head its READY packet names
|
|
70
70
|
(*The READY packet* in `skill://legion-worker/references/merge-gate.md`); a stale `Outcome` that no
|
|
71
71
|
longer describes the diff is a finding against the implementer, not a line the merger rewrites.
|
|
72
72
|
|
|
@@ -95,8 +95,8 @@ and the tester's proof below are both this proof.
|
|
|
95
95
|
the implementer's command or drives the same surface independently, and records the verdict in
|
|
96
96
|
`.legion/test.json` as `implementerProof` (`{verdict, how}`).
|
|
97
97
|
A test handoff whose predecessor carried no proof is a test failure, not a gap for the tester to fill:
|
|
98
|
-
record it in `failures` with `implementerProof.verdict: "rejected"`, complete the phase
|
|
99
|
-
the
|
|
98
|
+
record it in `failures` with `implementerProof.verdict: "rejected"`, complete the phase with
|
|
99
|
+
`verdict: "fail"`, and the daemon returns the issue to the implementer — the agent that developed the change owns
|
|
100
100
|
proving it (`handoff_write` for phase `test` refuses a rejected verdict, or `failed > 0`,
|
|
101
101
|
with no recorded failure). Otherwise, add your own proof before completing — a proof as defined
|
|
102
102
|
above — as the `E2E (tester)` line and the `proof` array `handoff_write` for
|
|
@@ -114,18 +114,17 @@ and the tester's proof below are both this proof.
|
|
|
114
114
|
`E2E` line's head to the new SHA with
|
|
115
115
|
`rebase re-check <old-sha> → <new-sha>: fingerprint unchanged, bare gates only`; the
|
|
116
116
|
real-surface verification is not repeated. Different: a full test round.
|
|
117
|
-
- **The implementer runs `skill://ce-simplify-code` once per pull request,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
the
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
reviewer's
|
|
128
|
-
in the `Thermo` line.
|
|
117
|
+
- **The implementer runs `skill://ce-simplify-code` once per pull request, in its first
|
|
118
|
+
implementing round, after its own proof and before it completes that round, when the diff
|
|
119
|
+
touches runtime code; a docs-only diff gets none.** The daemon starts no implementing round
|
|
120
|
+
between a clean review and the approval, so this is the one slot before the tester and the
|
|
121
|
+
reviewer read the head; a later round answering a review runs no second pass. It is scoped to
|
|
122
|
+
the pull request's own diff at that head: nothing applied leaves the head as it is; applied →
|
|
123
|
+
the implementer re-runs the repository's checks and its own proof on the applied head for the
|
|
124
|
+
surface the simplify diff touched, since a refactor that "preserves behaviour" is a claim until
|
|
125
|
+
it is executed, and re-cites the `CI` line and `E2E (implementer)` there before it completes.
|
|
126
|
+
That cost is why 0-applied is the expected outcome and a pass that applies is spent sparingly.
|
|
127
|
+
The reviewer's pair runs at the head each round reviews. Record both in the `Thermo` line.
|
|
129
128
|
- **No deferrals** is the body's rule (*PR body, review, and the merge gate* in
|
|
130
129
|
`skill://legion-worker`): the `Fast-follow:` line holds naming, duplication, or wording cleanup
|
|
131
130
|
only. A base frozen for others to stack on is never rewritten (*Rewriting pushed commits* in
|
|
@@ -35,8 +35,7 @@ Every path it cites is in sjawhar/legion.
|
|
|
35
35
|
your own `gh`; where no `legion` command is installed, use `gh api graphql` with the session's
|
|
36
36
|
GitHub credential and the fallback below.
|
|
37
37
|
In a Legion pane, the **implementer** runs the command after every push that answers a review
|
|
38
|
-
|
|
39
|
-
before its `handoff_complete`, and pastes its output, stamped with the head it just pushed, into
|
|
38
|
+
and before its `handoff_complete`, and pastes its output, stamped with the head it just pushed, into
|
|
40
39
|
the `Threads` section. The output is then recorded against the head the reviewer will read, and
|
|
41
40
|
nothing reads thread state before the implementer's completion. The command resolves each
|
|
42
41
|
unresolved thread whose newest submitted comment is the opener's own `Accepted:` reply. On a
|
|
@@ -80,8 +79,8 @@ Every path it cites is in sjawhar/legion.
|
|
|
80
79
|
|
|
81
80
|
Re-read `reviewThreads` and confirm that thread's `isResolved` is true. In either route, report
|
|
82
81
|
a refused resolution to the architect, which opens an ask for a human to resolve the thread by
|
|
83
|
-
hand — never skip it silently. The merger runs the command once more before
|
|
84
|
-
and does not
|
|
82
|
+
hand — never skip it silently. The merger runs the command once more before its READY
|
|
83
|
+
completion and does not complete while any `left open` line remains. That run is where every accepted
|
|
85
84
|
thread's resolution is guaranteed, since the merge queue's gate counts the unresolved threads at
|
|
86
85
|
the head. Acceptances posted after the implementer's last run are resolved here.
|
|
87
86
|
|
|
@@ -99,4 +98,4 @@ Every path it cites is in sjawhar/legion.
|
|
|
99
98
|
workflow passes on a re-run only once its threads are resolved, so you run `legion threads
|
|
100
99
|
resolve` (the daemon resolves the bot threads you accepted) and re-run the failed run before you
|
|
101
100
|
approve, as your role prompt says.
|
|
102
|
-
The merger resolves accepted threads that remain open before
|
|
101
|
+
The merger resolves accepted threads that remain open before its READY completion.
|