@sjawhar/opencode-legion-envoy 8.30.0 → 8.30.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.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/skills/envoy/SKILL.md +47 -286
  3. package/skills/envoy/references/check-settlement.md +62 -0
  4. package/skills/envoy/references/deliveries.md +62 -0
  5. package/skills/envoy/references/legion.md +12 -0
  6. package/skills/envoy/references/roles.md +10 -0
  7. package/skills/envoy/references/sources.md +41 -0
  8. package/skills/legion-architect/SKILL.md +41 -419
  9. package/skills/legion-architect/references/acceptance.md +153 -0
  10. package/skills/legion-architect/references/decomposition.md +77 -0
  11. package/skills/legion-architect/references/design-gate.md +148 -0
  12. package/skills/legion-architect/references/wake-routing.md +11 -6
  13. package/skills/legion-controller/SKILL.md +96 -469
  14. package/skills/legion-controller/references/admission.md +7 -6
  15. package/skills/legion-controller/references/backlog-stewardship.md +32 -27
  16. package/skills/legion-controller/references/daily-report.md +72 -0
  17. package/skills/legion-controller/references/escalation.md +37 -0
  18. package/skills/legion-controller/references/finding-asks.md +32 -14
  19. package/skills/legion-controller/references/session.md +98 -0
  20. package/skills/legion-controller/references/slack-mentions.md +10 -9
  21. package/skills/legion-controller/references/triage.md +81 -0
  22. package/skills/legion-controller/references/wakes.md +90 -0
  23. package/skills/legion-oracle/SKILL.md +26 -57
  24. package/skills/legion-retro/SKILL.md +116 -240
  25. package/skills/legion-retro/references/learnings.md +58 -0
  26. package/skills/legion-retro/references/pr-body-content.md +78 -0
  27. package/skills/legion-worker/SKILL.md +40 -462
  28. package/skills/legion-worker/references/conflicts-and-rewrites.md +2 -2
  29. package/skills/legion-worker/references/default-branch-check.md +16 -0
  30. package/skills/legion-worker/references/findings.md +2 -2
  31. package/skills/legion-worker/references/pr-body.md +1 -1
  32. package/skills/legion-worker/references/review-threads.md +1 -1
  33. package/skills/thermonuclear-code-quality/SKILL.md +34 -82
  34. package/skills/thermonuclear-deep-review/SKILL.md +34 -33
@@ -0,0 +1,153 @@
1
+ # Acceptance, retro, the merge and the close
2
+
3
+ Part of `skill://legion-architect`, sections 3, 5, 6 and 7. Read the section for the step you are
4
+ at.
5
+
6
+ ## Retro's result
7
+
8
+ Retro output is `docs/solutions/`, a last commit removing the issue's `.legion/<issue>/` from the
9
+ head that merges, the PR body content the repository's instructions derive from the pull request's
10
+ changed paths at that head, and one `dispatch message` on the issue. Retro writes no handoff and
11
+ never rewrites the reviewer-approved head.
12
+
13
+ When the implementer reports that it cannot compute that body content or do the work a line of it
14
+ affirms, that its read of the body failed or came back empty, or that GitHub refused the body, it
15
+ has pushed nothing and is still in its phase. Answer it with `envoy_publish` to its role topic,
16
+ saying how from the deployment instructions; when they do not say, open a `dispatch ask` naming the
17
+ missing instruction.
18
+
19
+ ## Accepting
20
+
21
+ ```text
22
+ dispatch comment --issue LEGION-40 --body-file - <<'EOF'
23
+ <acceptance: scope, integration evidence, the tester's handoff, both E2E lines, the approved head>
24
+ EOF
25
+ legion({ op: "sign_off", issue: "LEGION-40" })
26
+ ```
27
+
28
+ - The issue's Dispatch status stays `needs_review` in `accepting`; `sign_off` moves it to `retro`.
29
+ - Only what is beneath the issue counts for `SIGNOFF_CHILD_IN_WORKFLOW`: a child with nothing
30
+ beneath it is accepted while its own root runs.
31
+ - `request_backward_move` names the issue, because your grant serves the tree; the daemon starts
32
+ that phase's role with your reason in its task.
33
+ - `handoff_complete` from you in `accepting` is refused `ACCEPTANCE_IS_SIGN_OFF`. CI red at the head
34
+ in `accepting` returns the issue to `implementing` by the daemon's table.
35
+ - No `Production:` line is asked here: the production check is the implementer's, after the close.
36
+
37
+ ## The order after your acceptance
38
+
39
+ The daemon keeps this order from its fixed table; you start none of its steps:
40
+
41
+ 1. The tester's last result is green and the reviewer approves the head.
42
+ 2. The reviewer's approval of the head by SHA moves the issue to `accepting`; your `sign_off` moves
43
+ it to `retro`. The approved head still carries `.legion/<issue>/`.
44
+ 3. Retro commits its learnings under `docs/solutions/` on top of the approved head, then, as the
45
+ one final commit, the removal of `.legion/<issue>/`, so no merge carries a handoff onto the
46
+ default branch. Those commits do not void the approval and never return the tree to the tester
47
+ or reviewer.
48
+ 4. The merger quotes what sits above the approved head
49
+ (`jj diff --from <approved-sha> --to <tip-sha> --summary`) in READY and sends the READY packet
50
+ with its completion. The daemon refuses a head carrying anything above the approved head but
51
+ retro's commits (`READY_UNREVIEWED_CHANGE`) and a head that still carries `.legion/<issue>/`. It
52
+ posts `READY #<n> at <current sha> (approved at <approved sha>) for <KEY> (<pr url>)` on the
53
+ Dispatch issue, publishes it to the project's merge queue role when one is set, and submits the
54
+ merge of that head itself, as the implement App with GitHub's expected-head check. The
55
+ repository's required reviews and checks decide when it lands; Legion neither reads nor writes
56
+ those rules, and a review only a person can give is waited for, never bypassed. A READY the
57
+ design gate refused is submitted by the daemon once that spec version is approved.
58
+ 5. The merge lands under the repository's rules and closes the issue; a root's close waits until
59
+ the rest of its tree has left the workflow.
60
+
61
+ A refused or failed READY arrives while the merger holds `merging`, so the merger makes the move;
62
+ you tell it which (`envoy_publish` to its role topic). These targets are the merger's, not yours:
63
+ from `accepting` you send an issue back only to `implementing` or `testing`.
64
+
65
+ - The merger reports a failed verification: handle it as the `pr-blocked` row of
66
+ `skill://legion-architect/references/wake-routing.md` says. Tell the merger to move the issue
67
+ back with `request_backward_move`, naming what failed; never bypass.
68
+ - `READY_UNREVIEWED_CHANGE`, or a merger task with no approved head (`READY_NOT_APPROVED`): the
69
+ merger moves it back to `reviewing`, the reason naming the code and what it found, so the
70
+ reviewer runs a new round.
71
+ - Refused because the head still carries `.legion/<issue>/`: the merger moves it back to `retro`,
72
+ so the implementer's retro removes it.
73
+
74
+ ## A conflicting head
75
+
76
+ What returns the tree to review is a changed diff: a commit above the approved head that touches
77
+ anything outside `docs/solutions/` and `.legion/<issue>/`, or a conflict-resolution merge whose
78
+ fingerprint (the unchanged-diff check,
79
+ `skill://legion-worker/references/conflicts-and-rewrites.md`) differs from the approved head's.
80
+ Retro's commits and a merge forced by a GitHub-reported conflict whose fingerprint is unchanged do
81
+ not.
82
+
83
+ Make a conflict-resolution merge only when GitHub reports `CONFLICTING`
84
+ (`gh pr view <n> --json mergeable,mergeStateStatus`). Outside `awaiting_merge` the daemon records
85
+ a conflict and moves nothing, so the moment you see it:
86
+
87
+ - In a phase a worker holds, tell that worker (`envoy_publish` to its role topic) to move the issue
88
+ back to `implementing` with `request_backward_move`.
89
+ - In `accepting`, make the move yourself (`request_backward_move` with `issue`).
90
+ - In `awaiting_merge`, do nothing: the daemon withdraws the READY and sends the issue back to
91
+ `implementing` itself, telling you with a `checks-red` whose reason reads
92
+ `the head conflicts with <base>`.
93
+
94
+ The daemon then runs the phases from `implementing`: the implementer merges the bookmark forward
95
+ with the destination (`jj new legion/<KEY> <destination>`, the forward-merge procedure in
96
+ `skill://legion-worker/references/conflicts-and-rewrites.md`; never a rebase, which rewrites every
97
+ descendant of the fork point, including another tree's stacked branch), pushes it with
98
+ `jj git push` (a genuine fast-forward and a code push, so no `skip-checks: true` line) and posts the
99
+ before and after fingerprints; the tester re-runs the bare gates only; the reviewer approves the
100
+ new head by SHA, or continues its round if it had not approved; the daemon carries the issue
101
+ through your acceptance and retro to the merger's new READY.
102
+
103
+ ## A review thread GitHub will not resolve
104
+
105
+ GitHub lets only the pull request author's App resolve a thread, so the implementer resolves every
106
+ thread with plain `gh`: the ones it answers and the bot threads the reviewer accepted and named to
107
+ it. The reviewer resolves none. If the implementer reports that GitHub refused its
108
+ `resolveReviewThread`, open a `dispatch ask` naming the thread's id or URL and GitHub's message, for
109
+ a human to resolve it by hand, with options for resolved / could not. The merger does not complete
110
+ while that ask is open; READY itself checks no thread state
111
+ (`skill://legion-worker/references/merge-gate.md`).
112
+
113
+ ## The close
114
+
115
+ The merge closes the issue: when GitHub reports the pull request merged while the issue is in
116
+ `awaiting_merge` (a merge before that closes it the moment it gets there), the daemon writes `done`.
117
+
118
+ - A root whose merge lands while another issue of its tree is still in the workflow stays in
119
+ `awaiting_merge`, with its slot and workers kept, until the last of them is done or parked. One
120
+ `pr-merged` notice names what it waits for and none follows as they depart, so read the pull
121
+ request on GitHub.
122
+ - A child's close reaches you as `child-closed`. The daemon retires five of its six claims and
123
+ suspends its implementer, session and volume kept, because it still owes the production check
124
+ (`skill://legion-worker/references/merge-gate.md`, *The production check after the close*).
125
+ - A root's close writes `done`, frees the tree's admission slot and lingers the tree for
126
+ `linger_hours` with every member suspended. The linger's end, the tree's last step, waits until
127
+ every member's open production check has closed.
128
+
129
+ Nothing of the production check is yours. Its record lives on the pull request alone, since
130
+ Dispatch refuses a write on a closed issue; a defect it finds is a `dispatch ask`, never an issue;
131
+ and `read_record` shows a closed issue's open check (`check`: the pull request, the merge commit,
132
+ the looks, what woke the last one and when the next is due).
133
+
134
+ ## Hand-over at a root's linger
135
+
136
+ When a person closes a root and it stays `done` through its linger, each child still in the
137
+ workflow is handed to Legion as a root of its own: `todo` with the `legion` label, its phase, gate,
138
+ phase records and pull request carried into a new generation, and that phase's role started. The
139
+ child stays its own tree; reopening the old root does not take it back. Park a child with
140
+ `park_child` before then when its work should stop with the root.
141
+
142
+ If you are that child's architect:
143
+
144
+ - Your catch-up says `handed_over` and `carried`: resume from the phase, and never register a spec
145
+ or ask for approval again.
146
+ - It says `handed_over` alone (no gate to carry): your workers already run their phases, so
147
+ decompose nothing, and read the root's phase from the catch-up (for a root `held`, the phase its
148
+ `held` notice names). Before `awaiting_merge`, register your spec with `register_gate`: your
149
+ merger's READY waits for its approval. When that `design-approved` arrives, release and start
150
+ nothing: the workers are already running. A child carried in `accepting` is yours to accept. In
151
+ `awaiting_merge` the READY is through, so register none; a red or conflicting head that returns
152
+ the root to `implementing` makes its next READY read the gate again, so a later `ready-refused`
153
+ is when you register.
@@ -0,0 +1,77 @@
1
+ # Decomposing a tree
2
+
3
+ Part of `skill://legion-architect`, section 1. Read it before you read a child's handoff, create or
4
+ adopt children, split a change across repositories, or attach architecture components.
5
+
6
+ ## Reading the tree
7
+
8
+ Read the root issue, its acceptance criteria, its existing children and their current handoffs.
9
+ Your issue record is the `legion` tool's `read_record`. A child's committed handoff is a plain file
10
+ on its issue branch: read `.legion/<child>/<phase>.json` from that issue's workspace, or from GitHub:
11
+
12
+ ```bash
13
+ gh api repos/<owner>/<repo>/contents/.legion/<child>/<phase>.json?ref=legion/<child> --jq .content | base64 -d
14
+ ```
15
+
16
+ ## Adopting children
17
+
18
+ Put every adopted child into the first wave and release it with
19
+ `legion({ op: "release_children", issues: ["LEGION-41", "LEGION-42"] })`. The daemon runs nothing on
20
+ a child until it is released. Once it is released, the daemon moves it to `todo` and, while the
21
+ root's design gate is open, starts its phases itself. It starts no sub-architect, and you start no
22
+ worker.
23
+
24
+ ## Creating children
25
+
26
+ ```bash
27
+ dispatch issue --project <project> --parent <root issue> --title '<child outcome>' --spec-file - <<'EOF'
28
+ <acceptance criteria, scope, and context>
29
+ EOF
30
+ ```
31
+
32
+ - `--project` is the issue key's prefix before `-<n>` (`LEGSMOKE-3` → `LEGSMOKE`).
33
+ - In a root session `$LEGION_ISSUE` is the root (it equals `$LEGION_TREE`): pass it as `--parent`.
34
+ The daemon makes the sub-issue link from `--parent`.
35
+ - Group the returned keys into ordered waves: you release each wave by key with
36
+ `release_children`, and a child is inert until then.
37
+ - Do not pass `--assignee`, because the default keeps the tree's questions in one Inbox: under the
38
+ shared token a child inherits its parent's assignee (the human who answers the tree's asks), else
39
+ the project's default assignee, which a person sets on the Settings page; under a personal token
40
+ it goes to that token's owner (`dispatch whoami`). Set it only when a human told you a specific
41
+ person owns that child.
42
+ - Write into each child's acceptance that its pull request updates
43
+ `.dispatch/architecture/<id>.md` for each component whose code it changes, when the project has
44
+ architecture components (below).
45
+
46
+ A role token's `<project>` is not the `--project` above: it is the daemon's own project. The daemon
47
+ encodes a token as `legion-<project>-<key>-<role>` with the key lower-cased, so project `acme`,
48
+ issue `LEGION-41`, role `architect` is `legion-acme-legion-41-architect`.
49
+
50
+ ## Across repositories
51
+
52
+ A change across repositories gets one child per repository:
53
+ `dispatch issue --project <project> --parent <root> --repo <owner/name> …`. Choose the repository at
54
+ creation, because the daemon fixes it when it records the child.
55
+
56
+ Release a child that depends on another repository's change only after that change's pull request
57
+ merges, because the child's work must build on the merged change. Name the merged commit in its
58
+ task (`gh api repos/<owner>/<repo>/pulls/<n> --jq .merge_commit_sha`); for a repository that pins the
59
+ other one by commit (a lock file or dependency pin), the pin is that commit. Read the other
60
+ repositories with your gh's read-only access (`-R owner/repo`).
61
+
62
+ ## Architecture components
63
+
64
+ A project's architecture source is the repository whose `.dispatch/architecture/` files Dispatch
65
+ imports as its components (`skill://dispatch`, "Architecture components").
66
+
67
+ - **The components exist:** attach the root before you decompose, so every child inherits it:
68
+ `dispatch issue-update --issue <root> --components-json '{"mode":"explicit","ids":[…]}'`.
69
+ - **They do not exist yet (bootstrap):** in the root's first PR (the implement worker pushes it),
70
+ write `.dispatch/architecture/<id>.md` files for the planned components — front matter `title`,
71
+ `parent`, `depends_on`, `external`; no `paths` yet. The importer reads the source branch's head,
72
+ so leave the root on `inherit` until that PR has merged: an attach before then answers
73
+ `400 COMPONENTS_INPUT`, since the component does not exist. After the merge, run
74
+ `dispatch architecture-sync --project <KEY>` and attach the root; children created before then
75
+ inherit it.
76
+ - Give a child its own `--components-json` only when it changes a narrower set, and
77
+ `{"mode":"none","reason":"…"}` when it changes no code or file a component describes.
@@ -0,0 +1,148 @@
1
+ # The root spec and the design gate
2
+
3
+ Part of `skill://legion-architect`, section 1. Read it before you write the root spec, before any
4
+ edit to an approved root spec, and on `design-approved`, `design-changes-requested` or
5
+ `ready-refused`.
6
+
7
+ ## Writing the root spec
8
+
9
+ Specifications written into Dispatch follow `skill://dispatch`'s
10
+ [Writing a spec](skill://dispatch/SKILL.md#writing-a-spec). Wave releases, child closures and your
11
+ own status are visible from the issue tree and the handoffs; do not narrate them into the spec or a
12
+ `dispatch message`.
13
+
14
+ Extend the primary document in place: a new version adds only the evidence each decision needs and
15
+ what the human decides, each as a [decision block](skill://dispatch/SKILL.md#decision-blocks).
16
+ Never post a second "spec" artifact beside it: `dispatch artifact` with the primary document's name
17
+ replaces the human's document.
18
+
19
+ ## Which gate runs
20
+
21
+ The approval step runs only when the `Design gate policy` sentence of your `Legion addressing` line
22
+ says `gates.design: root-issues`. When it says `gates.design: off`, write the spec and register it
23
+ with `register_gate` at its current version, with no approval step: do not request approval and do
24
+ not wait for `design-approved`; the gate opens at registration. A sub-architect on a child issue
25
+ has no policy line and never runs the gate.
26
+
27
+ ## The armed gate
28
+
29
+ When the gate is armed, run this exact sequence before the tree's work starts:
30
+
31
+ 1. Extend the primary document in place:
32
+
33
+ ```bash
34
+ dispatch doc-edit --issue <root issue> --ops-json-file - <<'EOF'
35
+ ...
36
+ EOF
37
+ ```
38
+
39
+ 2. Settle every decision block as
40
+ [Approval of a spec](skill://dispatch/SKILL.md#approval-of-a-spec) says;
41
+ `dispatch request-approval` refuses while one is open. Each answer reaches you, since you follow
42
+ every ask you open.
43
+ 3. Run `task(agent="plan-gap-analyst")` with the current root spec, the code and command output it
44
+ relies on, and the human's words; resolve every finding in the root spec.
45
+ 4. Request approval only once the human has agreed to every point in the spec, because the human
46
+ approves what they already agreed to and a point new in the request has no decision block for
47
+ them to answer: give such a point its own decision block first, or take it out of the spec.
48
+
49
+ ```bash
50
+ dispatch request-approval --issue <root issue> --summary '<what the human is approving>'
51
+ ```
52
+
53
+ `--summary` says in one to three sentences what the human is approving and nothing else: no
54
+ commentary and no open question. The primary document is the default; the command prints
55
+ `Approval requested for spec.md (document id <UUID>) at version <N> (ask <id>)`.
56
+ 5. Register that document version with the daemon:
57
+
58
+ ```ts
59
+ legion({
60
+ op: "register_gate",
61
+ issue: "<root issue>",
62
+ artifactId: "<UUID>", // the document id the request printed
63
+ version: <N>, // the version the human is asked to approve
64
+ })
65
+ ```
66
+
67
+ Copy `artifactId` and `version` from what `dispatch request-approval` printed. The document id is
68
+ never the slug or file name you passed in (`spec`, `spec.md`): the daemon recognizes the
69
+ document's approval events by that id alone; the `legion` tool looks a slug or file name up in
70
+ Dispatch and hands the daemon the id.
71
+
72
+ Facts about the request:
73
+
74
+ - `dispatch request-approval` opens a system question with the fixed options `Approve` and
75
+ `Request changes`; a human answers it from the Inbox or approves from the document's header.
76
+ Never open a `dispatch ask` with an `Approve` option yourself: the daemon ignores its answer.
77
+ - Repeating the request while it waits on the human, with the same `--summary`, changes nothing and
78
+ returns it ("already waits on the human"); a different `--summary` is refused then.
79
+ - A newer version of the spec moves the open request to that version and leaves it waiting on you.
80
+ No wake tells you when the edit was yours. Once the human has agreed to every point in it, call
81
+ again to hand the same request back at the latest version.
82
+ - If the output reads `spec.md (document id <UUID>) is already approved at version <N>`, still call
83
+ `register_gate` with that id and version: the daemon reads the approval from Dispatch as it
84
+ registers, opens the gate and delivers `design-approved` at once. The same read covers a human
85
+ who answers between your two calls. You never approve anything yourself.
86
+
87
+ Then end your turn. Release no wave until a delivered wake shows `design-approved` on the root. On
88
+ `design-changes-requested`: revise the spec as the human's reason asks, run
89
+ `task(agent="plan-gap-analyst")` on the new version with the code, command output and the human's
90
+ words, resolve every finding, request approval again and end your turn.
91
+
92
+ ## After approval
93
+
94
+ Approval is pinned to the spec version: any new version closes the gate again, and no
95
+ `design-approved`-type wake says so; you know from your own edit, or from the `artifact.version`
96
+ event on your issue. The exception is the
97
+ version a human's own answer to a decision block writes when that answer is the whole change since
98
+ the version they approved: Dispatch records an `approved` review in their name at the new version
99
+ and emits `artifact.approved`, and the daemon reopens the gate and sends `design-approved`.
100
+
101
+ So edit an approved root spec only when its Summary, its Acceptance, the tree's scope, or a
102
+ decision a human settled in one of its decision blocks changes. Such a change is a point the human
103
+ has not agreed to: put the problem behind it to them as its own decision block, with its evidence,
104
+ at the end of the section it changes, and add nothing else to the spec. If you edit the block before
105
+ the human answers, the gate closes like any new version, but their answer still carries the approval
106
+ to the version it writes. Once they have answered:
107
+
108
+ - When the answer is the whole change, request nothing: the document's chip reads Approved at the
109
+ new version, its reviews dialog shows the review in their name, and the `design-approved` the
110
+ daemon sent opens nothing new. Carry on, and release no wave because of it.
111
+ - When the answer changes the Summary, the Acceptance or the tree's scope, rewrite that text and
112
+ repeat steps 3 to 5 on the new version, with a one-sentence `--summary` naming what changed since
113
+ the earlier approval (the card shows that approval, `Approved at version N on <date> by <who>.`,
114
+ and its `Show diff` control carries the detail). The rewrite closes the gate until they approve;
115
+ work the daemon started at the answer keeps running. Release no new wave until the `design-approved`
116
+ after the rewrite; work already in flight continues. Every merger's `READY` in the tree is
117
+ refused until a human approves the latest version.
118
+
119
+ A settled decision is the human's. A plan that would overturn one goes back to the planner with the
120
+ decision kept, unless the planner brings evidence the human did not weigh that would change it
121
+ (such as a measurement showing the settled choice cannot meet the Acceptance); then a decision
122
+ block names the decision and that evidence. A plan never overturns a settled decision on its own.
123
+
124
+ A design change that leaves the Summary, Acceptance, scope and settled decisions intact (a
125
+ planner's measurement that finds a better way to build the same outcome) goes in the plan (the
126
+ issue's `plan.md` document and `.legion/<issue>/plan.json`), never into the approved spec, even
127
+ where the spec's text describes the older design; the reviewer reads the plan beside the spec.
128
+
129
+ When a planner's `phase-finished` names a departure from the spec's design, compare it with the
130
+ approved spec. If the Summary, the Acceptance, the scope and every settled decision are unchanged,
131
+ the plan is the record and the tree carries on; otherwise change the root spec and request
132
+ approval again.
133
+
134
+ Later waves, re-scoping open children toward the same Acceptance, and integration-failure children
135
+ need no spec edit and no new approval. A child issue's spec is never gated.
136
+
137
+ ## A decision block for a read no pod can make
138
+
139
+ An acceptance criterion sometimes needs evidence from a production or external system before a
140
+ human can approve it: a measurement, a current count, a stored record. First check whether a
141
+ credential this tree's pods carry reaches it: the model route every pod has, and any identity the
142
+ operator's deployment grants pods. Read the pod's own environment (for example `AWS_CONFIG_FILE` or
143
+ `AGENT_SECRETS_URL`), because a credential can exist without any skill naming it.
144
+
145
+ When the evidence is not reachable, do not have another running session read it for this tree and
146
+ fold the result into the spec, because that hides the gap from the next tree that hits it. Write
147
+ the missing capability as its own decision block to the human (which external system, which read or
148
+ write, why the spec needs it), and leave it open until the human resolves it.
@@ -11,6 +11,11 @@ The four merge-notice rows are one sentence each; read
11
11
  `skill://legion-architect/references/merge-notices.md` for the notice that woke you — what its
12
12
  `reason` names, what the daemon has stopped doing, and what you read before you ask a person.
13
13
 
14
+ A "section N" below is that section of `skill://legion-architect`. The spec and gate steps of
15
+ section 1 are in `skill://legion-architect/references/design-gate.md`; the detail of sections 3, 6
16
+ and 7 (a conflict, the hand-over, the close) is in
17
+ `skill://legion-architect/references/acceptance.md`.
18
+
14
19
  | Wake | Procedure |
15
20
  | --- | --- |
16
21
  | `carried-gate-opened` | Only a root whose catch-up said `carried` gets it. Its `version` and `reason`: a human approved the old root's spec document at `version`, which opened the gate this root carried; the daemon has released any READY it refused and started any child waiting on it. It is not `design-approved`: do not proceed to section 2 or release a wave because of it, and start nothing. |
@@ -18,11 +23,11 @@ The four merge-notice rows are one sentence each; read
18
23
  | `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). |
19
24
  | `child-release-stalled` | Its `reason` names a child your `release_children` released while Dispatch already showed it `todo`, and that child's parent, whose release has not reached the daemon after three tries. Read the parent's record with the `legion` tool's `read_record` (`issue` the parent); run `release_children` for the child again once the parent is recorded in your tree, and tell the controller if it never is. |
20
25
  | `child-not-recorded` | Its `reason` names a child your tree did not record and why: its repository resolves to none (neither it nor an ancestor names one, and the project has no default), or to one not mapped to the project, or to one the daemon leaves out for a missing GitHub App. The daemon said why on the child too. Set the child's repository to one of the project's (`dispatch issue-update --repo owner/name`), then release it again with `release_children`; tell the controller when the repository it needs is not served. |
21
- | `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 — except on a root whose catch-up said `handed_over`: its workers already run their phases, so start nothing; the approval releases any READY the gate refused. One whose `version` is the version a human's own answer to a decision block wrote, after an earlier approval (Dispatch carried that approval to it, section 1), opens nothing new: carry on, and release no wave because of it. |
22
- | `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. |
23
- | `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 issue is in `accepting` and the acceptance is yours (section 6): read the evidence, then `sign_off` or send it back with `request_backward_move`. 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. |
24
- | `pr-blocked` | Its `reason` names the pull request and the `max_fix_attempts` it reached, or reads `Issue reached review_round_cap=<N>.` (the end of this row). The count is of heads pushed onto a red verdict 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, and no later CI red within the attempt that ran the count out (the counted head, and every head pushed on top of it until a push lands on a head CI judged green, the tester's red tests and handoff onto the held red included) sends the work back from `testing`, `reviewing`, `accepting`, `retro` or `merging` either: the issue stays in its phase until you move it, or a worker's own decision does (a tester's `fail`, a reviewer's request for changes). A head pushed onto green CI leaves that attempt, so its red still comes as a `checks-red`. In `implementing`, give the implementer the failing checks (`envoy_publish` to its role topic). In any later phase a worker holds, tell that worker (`envoy_publish` to its role topic) to move the issue back to `implementing` with `request_backward_move`, naming the failing checks, and the daemon starts the implementer; or file a corrective child. In `accepting`, where no worker holds a phase, the move is yours: `request_backward_move` with `issue`, naming the failing checks. In `awaiting_merge`, where no worker holds a phase and a backward move is refused, open a `dispatch ask` naming the failing checks for the human who merges, as section 6 does for a conflict there. Do not treat the blocked PR as final. A `review_round_cap` reason is the implementer's round count instead: every move back to an earlier phase counts one, and the notice comes again with each round past the cap. It moves nothing either, since the round that reached the cap has already started its worker. Tell the rounds apart by what announced each: a tester's fail by its `phase-finished` with `verdict: "fail"`; a request for changes by the reviewer's `phase-finished` over a `CHANGES_REQUESTED` review; the daemon's own move by a `checks-red` notice, whose reason names a conflict (`the head conflicts with <base>`), the required checks that failed (`CI is red at <head>`), or the merge queue's failed merge and its failing run (`the merge queue took pull request #<n> out at READY's head …`); a worker's `request_backward_move` by a `phase-finished` from the role that asked, whose reason reaches only the task of the worker the move started, so ask that worker; your own send-back from `accepting` by the reason you gave. Judge the cap by the rounds that are not conflicts. Conflict returns need nothing from you beyond the next round. Tester fails, requests for changes and CI-red returns (a `checks-red` notice is raised for CI only when a required check fails on code the branch pushed, or when the merge queue fails the merge) are the rounds the cap is for. Each `review_round_cap` notice is a decision you make in the turn it wakes, never one left for the next round to make: read what every counted round found, choose one of the three below, act on it in the same turn, and post it as a `dispatch comment` on the issue naming the rounds you read and the choice. **Narrow**: the change itself is sound but each round finds more around it; tell the implementer and the reviewer (`envoy_publish` to each one's role topic) which acceptance lines the next round answers; a correctness finding is still fixed in this pull request, and only naming, duplication or wording cleanup moves to the `Fast-follow:` line. **Split**: part of the work cannot settle in this pull request. On a child, file that part as a child of this issue's parent, never of this one (a parent waits in `accepting` for every child, and the split part usually builds on this pull request's unmerged code): `dispatch issue --project <project> --parent <parent> --title '<outcome>' --blocked-by <this issue> --spec-file -`, released with `release_children` once this issue closes. On the root, narrow it to what settles here and open a `dispatch ask` for the rest (`skill://legion-worker/references/findings.md`): a person decides whether that becomes an issue. Tell the implementer and the reviewer that this pull request no longer carries it. **Accept the rounds**: each round found less than the one before it, and the last found no defect in the change itself (a stale line, a comment that states an old default); let the loop run and say why in the comment. A later notice is a new decision: accept again only when its round found less than the round you last accepted, and otherwise narrow or split. A finding that changes what the spec accepts is a decision block in the root spec (section 1), and `dispatch ask` is only for a decision that needs a human's authority; neither replaces the choice. Judge a worker's backward move by the reason that worker gives. A move you ordered for a conflict found before `awaiting_merge` (section 6) is a conflict return. The merger's move back to `reviewing` for a READY refusal (`READY_UNREVIEWED_CHANGE`, or `READY_NOT_APPROVED` when its task names no approved head), and the reviewer's no-fix request for changes that follows it so the tester runs the head, are not the work going in circles either. A move that sends back code found wrong counts with the tester fails. |
25
- | `pr-merged` | The pull request merged and the issue did not close; its `reason` says why. Merged before the issue reached `awaiting_merge`: the workflow runs on, and the merge closes the issue the moment it gets there, your acceptance and retro first. Merged while the rest of the tree still runs: the reason names the pull request and every issue the close waits for, and the root waits in `awaiting_merge`, slot and workers kept, until the last of them is done or parked (section 7). Either way you start nothing and no further notice follows. |
26
+ | `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 — except on a root whose catch-up said `handed_over`: its workers already run their phases, so start nothing; the approval releases any READY the gate refused. One whose `version` is the version a human's own answer to a decision block wrote, after an earlier approval (Dispatch carried that approval to it, "After approval" in `skill://legion-architect/references/design-gate.md`), opens nothing new: carry on, and release no wave because of it. |
27
+ | `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 "The armed gate" in `skill://legion-architect/references/design-gate.md` says; end your turn; the gate is closed. |
28
+ | `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 the full condition in "After approval" of `skill://legion-architect/references/design-gate.md`: 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 that section says. A `reviewer` notice whose GitHub review is `CHANGES_REQUESTED` needs nothing from you: the daemon has returned the issue to `implementing` (Dispatch `in_progress`) and started the **implementer**, whose correction goes through the tester and the reviewer again, never straight to retro. A `reviewer` notice with an `APPROVED` review means the issue is in `accepting` and the acceptance is yours (section 6): read the evidence, then `sign_off` or send it back with `request_backward_move`. 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. |
29
+ | `pr-blocked` | Its `reason` names the pull request and the `max_fix_attempts` it reached, or reads `Issue reached review_round_cap=<N>.` (the end of this row). The count is of heads pushed onto a red verdict 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, and no later CI red within the attempt that ran the count out (the counted head, and every head pushed on top of it until a push lands on a head CI judged green or on a settled head whose base branch requires no check and no workflow, the tester's red tests and handoff onto the held red included) sends the work back from `testing`, `reviewing`, `accepting`, `retro` or `merging` either: the issue stays in its phase until you move it, or a worker's own decision does (a tester's `fail`, a reviewer's request for changes). A head pushed onto green CI, or onto a settled head whose base branch requires no check and no workflow, leaves that attempt, so its red still comes as a `checks-red`. In `implementing`, give the implementer the failing checks (`envoy_publish` to its role topic). In any later phase a worker holds, tell that worker (`envoy_publish` to its role topic) to move the issue back to `implementing` with `request_backward_move`, naming the failing checks, and the daemon starts the implementer; or file a corrective child. In `accepting`, where no worker holds a phase, the move is yours: `request_backward_move` with `issue`, naming the failing checks. In `awaiting_merge`, where no worker holds a phase and a backward move is refused, open a `dispatch ask` naming the failing checks for the human who merges. Do not treat the blocked PR as final. A `review_round_cap` reason is the implementer's round count instead: every move back to an earlier phase counts one, and the notice comes again with each round past the cap. It moves nothing either, since the round that reached the cap has already started its worker. Tell the rounds apart by what announced each: a tester's fail by its `phase-finished` with `verdict: "fail"`; a request for changes by the reviewer's `phase-finished` over a `CHANGES_REQUESTED` review; the daemon's own move by a `checks-red` notice, whose reason names a conflict (`the head conflicts with <base>`), the required checks that failed (`CI is red at <head>`), or the merge queue's failed merge and its failing run (`the merge queue took pull request #<n> out at READY's head …`); a worker's `request_backward_move` by a `phase-finished` from the role that asked, whose reason reaches only the task of the worker the move started, so ask that worker; your own send-back from `accepting` by the reason you gave. Judge the cap by the rounds that are not conflicts. Conflict returns need nothing from you beyond the next round. Tester fails, requests for changes and CI-red returns (a `checks-red` notice is raised for CI only when a required check fails on code the branch pushed, or when the merge queue fails the merge) are the rounds the cap is for. Each `review_round_cap` notice is a decision you make in the turn it wakes, never one left for the next round to make: read what every counted round found, choose one of the three below, act on it in the same turn, and post it as a `dispatch comment` on the issue naming the rounds you read and the choice. **Narrow**: the change itself is sound but each round finds more around it; tell the implementer and the reviewer (`envoy_publish` to each one's role topic) which acceptance lines the next round answers; a correctness finding is still fixed in this pull request, and only naming, duplication or wording cleanup moves to the `Fast-follow:` line. **Split**: part of the work cannot settle in this pull request. On a child, file that part as a child of this issue's parent, never of this one (a parent waits in `accepting` for every child, and the split part usually builds on this pull request's unmerged code): `dispatch issue --project <project> --parent <parent> --title '<outcome>' --blocked-by <this issue> --spec-file -`, released with `release_children` once this issue closes. On the root, narrow it to what settles here and open a `dispatch ask` for the rest (`skill://legion-worker/references/findings.md`): a person decides whether that becomes an issue. Tell the implementer and the reviewer that this pull request no longer carries it. **Accept the rounds**: each round found less than the one before it, and the last found no defect in the change itself (a stale line, a comment that states an old default); let the loop run and say why in the comment. A later notice is a new decision: accept again only when its round found less than the round you last accepted, and otherwise narrow or split. A finding that changes what the spec accepts is a decision block in the root spec ("After approval" in `skill://legion-architect/references/design-gate.md`), and `dispatch ask` is only for a decision that needs a human's authority; neither replaces the choice. Judge a worker's backward move by the reason that worker gives. A move you ordered for a conflict found before `awaiting_merge` ("A conflicting head" in `skill://legion-architect/references/acceptance.md`) is a conflict return. The merger's move back to `reviewing` for a READY refusal (`READY_UNREVIEWED_CHANGE`, or `READY_NOT_APPROVED` when its task names no approved head), and the reviewer's no-fix request for changes that follows it so the tester runs the head, are not the work going in circles either. A move that sends back code found wrong counts with the tester fails. |
30
+ | `pr-merged` | The pull request merged and the issue did not close; its `reason` says why. Merged before the issue reached `awaiting_merge`: the workflow runs on, and the merge closes the issue the moment it gets there, your acceptance and retro first. Merged while the rest of the tree still runs: the reason names the pull request and every issue the close waits for, and the root waits in `awaiting_merge`, slot and workers kept, until the last of them is done or parked ("The close" in `skill://legion-architect/references/acceptance.md`). Either way you start nothing and no further notice follows. |
26
31
  | `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. |
27
32
  | `merge-refused` | GitHub refused the daemon's submission of READY's merge in a way a retry would meet again, so it submits that merge no further: ask a person with `dispatch ask` to fix the repository setting or App permission and merge at READY's head (`skill://legion-architect/references/merge-notices.md#merge-refused`). |
28
33
  | `merge-stalled` | GitHub shows the pull request closed, or at another head, while the record still awaits READY's head, and that change's webhook never reached the daemon: read the pull request on GitHub and act on what it shows (`skill://legion-architect/references/merge-notices.md#merge-stalled`). |
@@ -31,4 +36,4 @@ The four merge-notice rows are one sentence each; read
31
36
  | A reply on an ask you follow | Interpret the reply in the issue's design context. Answer in its thread (`dispatch comment --reply-to <comment id>`; under an open ask whose next move is yours, such as the approval request you must revise or hand back, `--reply-to-ask <ask id> --turn agent`, since a default-turn reply hands that request back to the human and a corrected `--summary` is then refused), then adjust the plan or relay it via `envoy_publish` to the responsible worker's role token; scope and product decisions remain with you. |
32
37
  | `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. |
33
38
  | `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. |
34
- | `ready-refused` | Its `version` and `reason`. The merger's READY was refused, and its packet is kept with the completion so no second READY is ever needed. With `version` greater than 0, `reason` reads `READY refused: approve design version <N> before requesting READY.`: the root's spec is already registered at that version, so get it approved (section 1), and the daemon advances the merge and posts the kept packet the moment it is. With `version` 0, `reason` reads `READY refused: no design version is approved; register and approve the tree's spec before requesting READY.`: the root has no spec registered yet, so register it and get it approved (section 1) exactly as above; the daemon releases the kept packet the same way — at registration itself when that opens the gate (`gates.design: off`, or Dispatch already shows the version approved), otherwise the moment a human approves the version you registered. Either way, nothing else is needed once the gate opens. On a root whose catch-up said `carried`, the gate is a copy on the old root's spec document: run nothing of section 1; tell the controller the READY waits on a human's approval of that document, and `carried-gate-opened` follows the approval. A `reason` starting `READY_PACKET_MISSING` names a READY refused before the daemon kept packets: that READY is void and the issue stays in merging, so start it over (`park_child` then `rerun_child`, for a child) or end it. |
39
+ | `ready-refused` | Its `version` and `reason`. The merger's READY was refused, and its packet is kept with the completion so no second READY is ever needed. With `version` greater than 0, `reason` reads `READY refused: approve design version <N> before requesting READY.`: the root's spec is already registered at that version, so get it approved (`skill://legion-architect/references/design-gate.md`), and the daemon advances the merge and posts the kept packet the moment it is. With `version` 0, `reason` reads `READY refused: no design version is approved; register and approve the tree's spec before requesting READY.`: the root has no spec registered yet, so register it and get it approved (`skill://legion-architect/references/design-gate.md`) exactly as above; the daemon releases the kept packet the same way — at registration itself when that opens the gate (`gates.design: off`, or Dispatch already shows the version approved), otherwise the moment a human approves the version you registered. Either way, nothing else is needed once the gate opens. On a root whose catch-up said `carried`, the gate is a copy on the old root's spec document: run nothing of `skill://legion-architect/references/design-gate.md`; tell the controller the READY waits on a human's approval of that document, and `carried-gate-opened` follows the approval. A `reason` starting `READY_PACKET_MISSING` names a READY refused before the daemon kept packets: that READY is void and the issue stays in merging, so start it over (`park_child` then `rerun_child`, for a child) or end it. |