@sjawhar/claude-envoy 0.0.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +6 -0
- package/.mcp.json +11 -0
- package/.omp-plugin/plugin.json +6 -0
- package/AGENTS.md +343 -0
- package/README.md +639 -0
- package/bin/dispatch +6 -0
- package/bin/envoy-channel.ts +9 -0
- package/bin/envoy-send.ts +33 -0
- package/dist/THIRD_PARTY_NOTICES +816 -0
- package/dist/dispatch.js +27707 -0
- package/dist/envoy-channel.js +57695 -0
- package/dist/session-hook.js +26328 -0
- package/dist/session-title-hook.js +116 -0
- package/hooks/hooks.json +60 -0
- package/hooks/session-hook.ts +512 -0
- package/hooks/session-title-hook.ts +46 -0
- package/legion/.claude-plugin/plugin.json +4 -0
- package/legion/agents/deep-worker.md +68 -0
- package/legion/agents/oracle.md +35 -0
- package/legion/agents/plan-gap-analyst.md +65 -0
- package/legion/agents/plan-reviewer.md +104 -0
- package/legion/agents/thermonuclear-code-quality.md +30 -0
- package/legion/agents/thermonuclear-deep-review.md +30 -0
- package/legion/hooks/hooks.json +89 -0
- package/legion/skills/ce-simplify-code/LICENSE +21 -0
- package/legion/skills/ce-simplify-code/SKILL.md +66 -0
- package/legion/skills/ce-simplify-code/references/personas/code-quality-reviewer.md +17 -0
- package/legion/skills/ce-simplify-code/references/personas/code-reuse-reviewer.md +7 -0
- package/legion/skills/ce-simplify-code/references/personas/efficiency-reviewer.md +11 -0
- package/legion/skills/leading-a-work-stream/SKILL.md +96 -0
- package/legion/skills/leading-a-work-stream/references/daily-plan.md +8 -0
- package/legion/skills/leading-a-work-stream/references/decision-rights.md +6 -0
- package/legion/skills/leading-a-work-stream/references/direction-and-coaching.md +13 -0
- package/legion/skills/leading-a-work-stream/references/dispatch-upkeep.md +8 -0
- package/legion/skills/leading-a-work-stream/references/sources.md +9 -0
- package/legion/skills/leading-a-work-stream/references/speed-tick.md +44 -0
- package/legion/skills/leading-a-work-stream/references/user-quotes.md +7 -0
- package/legion/skills/legion-architect/SKILL.md +86 -0
- package/legion/skills/legion-architect/references/acceptance.md +208 -0
- package/legion/skills/legion-architect/references/decomposition.md +129 -0
- package/legion/skills/legion-architect/references/design-gate.md +206 -0
- package/legion/skills/legion-architect/references/merge-notices.md +91 -0
- package/legion/skills/legion-architect/references/wake-routing.md +41 -0
- package/legion/skills/legion-controller/SKILL.md +151 -0
- package/legion/skills/legion-controller/references/admission.md +22 -0
- package/legion/skills/legion-controller/references/backlog-stewardship.md +101 -0
- package/legion/skills/legion-controller/references/daily-report.md +75 -0
- package/legion/skills/legion-controller/references/escalation.md +82 -0
- package/legion/skills/legion-controller/references/finding-asks.md +181 -0
- package/legion/skills/legion-controller/references/parked-decisions.md +198 -0
- package/legion/skills/legion-controller/references/session.md +100 -0
- package/legion/skills/legion-controller/references/slack-mentions.md +154 -0
- package/legion/skills/legion-controller/references/triage.md +96 -0
- package/legion/skills/legion-controller/references/wakes.md +132 -0
- package/legion/skills/legion-oracle/SKILL.md +48 -0
- package/legion/skills/legion-retro/SKILL.md +112 -0
- package/legion/skills/legion-retro/references/learnings.md +60 -0
- package/legion/skills/legion-retro/references/pr-body-content.md +56 -0
- package/legion/skills/legion-worker/SKILL.md +69 -0
- package/legion/skills/legion-worker/references/cleanup-deletion.md +22 -0
- package/legion/skills/legion-worker/references/conflicts-and-rewrites.md +164 -0
- package/legion/skills/legion-worker/references/default-branch-check.md +53 -0
- package/legion/skills/legion-worker/references/findings.md +199 -0
- package/legion/skills/legion-worker/references/merge-gate.md +353 -0
- package/legion/skills/legion-worker/references/pr-body.md +218 -0
- package/legion/skills/legion-worker/references/review-threads.md +77 -0
- package/legion/skills/legion-worker/references/systematic-rename.md +19 -0
- package/legion/skills/legion-worker/references/tools.md +708 -0
- package/legion/skills/thermonuclear-code-quality/LICENSE +21 -0
- package/legion/skills/thermonuclear-code-quality/SKILL.md +60 -0
- package/legion/skills/thermonuclear-deep-review/LICENSE +21 -0
- package/legion/skills/thermonuclear-deep-review/SKILL.md +80 -0
- package/package.json +49 -1
- package/scripts/build-inputs.txt +84 -0
- package/scripts/build.ts +415 -0
- package/scripts/check-packed-skills.sh +50 -0
- package/scripts/pack-skills.sh +146 -0
- package/scripts/smoke-channel.sh +178 -0
- package/scripts/smoke-clear-rebind.sh +164 -0
- package/scripts/smoke-session-title.sh +245 -0
- package/scripts/verify-build.sh +88 -0
- package/skills/agent-secrets/SKILL.md +180 -0
- package/skills/asd-ste100/LICENSE +21 -0
- package/skills/asd-ste100/SKILL.md +140 -0
- package/skills/asd-ste100/examples/before-after.md +92 -0
- package/skills/asd-ste100/references/writing-rules.md +59 -0
- package/skills/dispatch/SKILL.md +89 -0
- package/skills/dispatch-first/SKILL.md +46 -0
- package/skills/dispatch-writing-specs/SKILL.md +80 -0
- package/skills/envoy/SKILL.md +82 -0
- package/skills/envoy/references/check-settlement.md +62 -0
- package/skills/envoy/references/deliveries.md +67 -0
- package/skills/envoy/references/legion.md +14 -0
- package/skills/envoy/references/roles.md +12 -0
- package/skills/envoy/references/sources.md +41 -0
- package/skills/outside-message/SKILL.md +111 -0
- package/skills/plain-words/SKILL.md +88 -0
- package/src/channel-forwarder.ts +262 -0
- package/src/claude-channel.ts +705 -0
- package/src/claude-session.ts +38 -0
- package/src/envoy-channel-server.ts +2115 -0
- package/src/envoy-client.ts +26 -0
- package/src/envoy-off.ts +62 -0
- package/src/legion-claim.ts +211 -0
- package/src/legion-inbox.ts +110 -0
- package/src/legion-phase-hook.ts +554 -0
- package/src/legion-tool.ts +69 -0
- package/src/replay-state.ts +97 -0
- package/src/send-arguments.ts +13 -0
- package/src/session-identity.ts +165 -0
- package/src/session-state.ts +142 -0
- package/src/session-title.ts +251 -0
- package/src/subscription-topics.ts +16 -0
|
@@ -0,0 +1,129 @@
|
|
|
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 handoffs are records on its
|
|
10
|
+
Dispatch issue: read the newest of a phase with
|
|
11
|
+
`dispatch handoff-read --issue <child> --phase <word>` (`plan`, `implement`, `test` or `review`),
|
|
12
|
+
and an earlier one with `--version <n>`. The child's Conversation lists every version.
|
|
13
|
+
|
|
14
|
+
## Adopting children
|
|
15
|
+
|
|
16
|
+
Put every adopted child into the first wave and release it with
|
|
17
|
+
`legion({ op: "release_children", issues: ["LEGION-41", "LEGION-42"] })`. The daemon runs nothing on
|
|
18
|
+
a child until it is released. Once it is released, the daemon moves it to `todo` and, while the
|
|
19
|
+
root's design gate is open, starts its phases itself. It starts no sub-architect, and you start no
|
|
20
|
+
worker. A release is accepted whole whatever the `--blocked-by` links say: its answer names
|
|
21
|
+
`started` and `held` (each held child with the issues it waits on) beside `released`, and a held
|
|
22
|
+
child waits in your tree until Legion starts it.
|
|
23
|
+
|
|
24
|
+
## Creating children
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
dispatch issue --project <project> --parent <root issue> --title '<child outcome>' --spec-file - <<'EOF'
|
|
28
|
+
<the child's spec, written as skill://dispatch-writing-specs says, scaled to the child>
|
|
29
|
+
EOF
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- Write each child's spec as `skill://dispatch-writing-specs` says, scaled to the child: its
|
|
33
|
+
outline (a few sentences a part for a small child), each requirement marked as a `requirement`
|
|
34
|
+
block, a proposed
|
|
35
|
+
answer to each point, each term a new reader may not know explained, no item number from another
|
|
36
|
+
document (name a requirement of the root by what it says), and no text copied from the root that
|
|
37
|
+
does not apply to this child. Write the child's Verification document with it, one `check` block
|
|
38
|
+
per thing that must be proved. The child's workers read only this spec and those checks.
|
|
39
|
+
- Before `dispatch issue` creates any child, dispatch a fresh red-team subagent
|
|
40
|
+
(`task(agent="oracle")`) with the
|
|
41
|
+
root spec, the tree's open children with their repositories and pull requests, and the proposed
|
|
42
|
+
child list, each child's title and whole spec. Its mandate: *"You did not write this list. Argue
|
|
43
|
+
that each child should not exist. Name every child that is a to-do, a fix found in review, a
|
|
44
|
+
proof of the root's acceptance, or a part of the architect's own work, and say where it folds:
|
|
45
|
+
the root's pull request or an open child's."* `wait` for its verdict, since it runs as a job of
|
|
46
|
+
its own. A child survives only when it cannot ride in the
|
|
47
|
+
root's pull request or an open child's: its code is in a repository no open pull request of this
|
|
48
|
+
tree is in, or it is its own deploy: it must be applied on its own before other work of the tree
|
|
49
|
+
can build on it.
|
|
50
|
+
A repeated change — the same edit per program, directory or wave of this tree's own work —
|
|
51
|
+
survives as one child, never one per instance, and a deletion of what this tree replaced rides
|
|
52
|
+
in the pull request of the last change it waits on, never a child of its own: a wave is an order
|
|
53
|
+
of merges, not a deliverable.
|
|
54
|
+
Post the verdict, with the folds you made, as one `dispatch comment` on your own issue (the root,
|
|
55
|
+
in a root session): the architect
|
|
56
|
+
who wrote the list reads every child on it as needed, and the comment stops a later session
|
|
57
|
+
filing the same children again.
|
|
58
|
+
- Then paste each survivor's whole spec into a fresh `task(agent="plan-gap-analyst")` to check it
|
|
59
|
+
against `skill://dispatch-writing-specs`, `wait` for its result, and fix what it flags: your own
|
|
60
|
+
reread misses the terms
|
|
61
|
+
and item numbers you already know. File only the survivors, and reply under the verdict comment
|
|
62
|
+
with each one's key.
|
|
63
|
+
- Before `release_children`, check that every child you filed is named under a verdict comment,
|
|
64
|
+
because a child filed without one skipped the check. A child a human created is adopted as it is
|
|
65
|
+
and needs none.
|
|
66
|
+
- `--project` is the issue key's prefix before `-<n>` (`LEGSMOKE-3` → `LEGSMOKE`).
|
|
67
|
+
- In a root session `$LEGION_ISSUE` is the root (it equals `$LEGION_TREE`): pass it as `--parent`.
|
|
68
|
+
The daemon makes the sub-issue link from `--parent`.
|
|
69
|
+
- Order the returned keys into waves with `--blocked-by` links (a later wave's child blocked by the
|
|
70
|
+
children it builds on), then release every child at once with `release_children`: Legion holds a
|
|
71
|
+
released child until its last blocker closes, and a child is inert only until it is released.
|
|
72
|
+
- Never release a child `--blocked-by` its own parent: the parent now merges last, so that child
|
|
73
|
+
would wait forever.
|
|
74
|
+
- Do not pass `--assignee`, because the default keeps the tree's questions in one Inbox: under the
|
|
75
|
+
shared token a child inherits its parent's assignee (the human who answers the tree's asks), else
|
|
76
|
+
the project's default assignee, which a person sets on the Settings page; under a personal token
|
|
77
|
+
it goes to that token's owner (`dispatch whoami`). Set it only when a human told you a specific
|
|
78
|
+
person owns that child.
|
|
79
|
+
- Write a `check` of each child for the update of
|
|
80
|
+
`.dispatch/architecture/<id>.md` for each component whose code its pull request changes, when
|
|
81
|
+
the project has architecture components (below).
|
|
82
|
+
|
|
83
|
+
A role token's `<project>` is not the `--project` above: it is the daemon's own project. The daemon
|
|
84
|
+
encodes a token as `legion-<project>-<key>-<role>` with the key lower-cased, so project `acme`,
|
|
85
|
+
issue `LEGION-41`, role `architect` is `legion-acme-legion-41-architect`.
|
|
86
|
+
|
|
87
|
+
## Across repositories
|
|
88
|
+
|
|
89
|
+
A change across repositories gets one child per repository:
|
|
90
|
+
`dispatch issue --project <project> --parent <root> --repo <owner/name> …`. Choose the repository at
|
|
91
|
+
creation, because the daemon fixes it when it records the child.
|
|
92
|
+
|
|
93
|
+
Release a child that depends on another repository's change only after that change's pull request
|
|
94
|
+
merges, because the child's work must build on the merged change. Name the merged commit in its
|
|
95
|
+
task (`gh api repos/<owner>/<repo>/pulls/<n> --jq .merge_commit_sha`); for a repository that pins the
|
|
96
|
+
other one by commit (a lock file or dependency pin), the pin is that commit. Do not hold such a
|
|
97
|
+
child with `--blocked-by` instead, because Legion would start it at the blocker's close, before you
|
|
98
|
+
name the merged commit. Read the other repositories with your gh's read-only access
|
|
99
|
+
(`-R owner/repo`).
|
|
100
|
+
|
|
101
|
+
## A root with no code of its own
|
|
102
|
+
|
|
103
|
+
When every change of the tree lands through its children, the root's branch has no commit of its
|
|
104
|
+
own, and GitHub opens no pull request for a branch with no commits ahead of its base: implementing
|
|
105
|
+
would never end. The root's implementer then pushes one empty commit
|
|
106
|
+
(`jj new -m '<KEY>: the root of its children; no code of its own'`, with no trailer) and opens the
|
|
107
|
+
pull request on it, as its role prompt says; that push starts the required checks like any other.
|
|
108
|
+
Plan no code for the root in that case, and do not create a child only to give the root a commit.
|
|
109
|
+
|
|
110
|
+
## Architecture components
|
|
111
|
+
|
|
112
|
+
A project's architecture sources are the repositories whose `.dispatch/architecture/` files
|
|
113
|
+
Dispatch imports as its components (`skill://dispatch`, "Architecture components").
|
|
114
|
+
|
|
115
|
+
- **The components exist:** attach the root before you decompose, so every child inherits it:
|
|
116
|
+
`dispatch issue-update --issue <root> --components-json '{"mode":"explicit","ids":[…]}'`.
|
|
117
|
+
- **They do not exist yet (bootstrap):** in the root's first PR (the implement worker pushes it),
|
|
118
|
+
write `.dispatch/architecture/<id>.md` files for the planned components — front matter `title`,
|
|
119
|
+
`parent`, `depends_on`, `external`; no `paths` yet. A project may have several sources, one per
|
|
120
|
+
repository, and its model is all of their components together: the file goes in the branch of
|
|
121
|
+
the source whose repository holds it, `parent` and `depends_on` name components of that same
|
|
122
|
+
source only, and its id must be one no other source of the project uses (a repeated id fails that
|
|
123
|
+
source's import, naming the repository that has it). The importer reads the source branch's
|
|
124
|
+
head, so leave the root on `inherit` until that PR has merged: an attach before then answers
|
|
125
|
+
`400 COMPONENTS_INPUT`, since the component does not exist. After the merge, run
|
|
126
|
+
`dispatch architecture-sync --project <KEY>` (every source; `--repo <owner/name>` syncs that one
|
|
127
|
+
alone) and attach the root; children created before then inherit it.
|
|
128
|
+
- Give a child its own `--components-json` only when it changes a narrower set, and
|
|
129
|
+
`{"mode":"none","reason":"…"}` when it changes no code or file a component describes.
|
|
@@ -0,0 +1,206 @@
|
|
|
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
|
|
10
|
+
[`dispatch-writing-specs`](skill://dispatch-writing-specs/SKILL.md#the-outline). 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#asks-and-ask-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
|
+
Mark each requirement of the spec as a `requirement` block, and write the issue's Verification
|
|
20
|
+
document (`verification.md`) with it: one `check` block per thing that must be proved, each with
|
|
21
|
+
`serves` naming the requirements it serves, its `surface`, its `run`, its `when` and its
|
|
22
|
+
`negative_control`. The human approves both documents in one request (step 4 below), and every
|
|
23
|
+
later role reads the checks: the implementer and the tester record a result against each, and the
|
|
24
|
+
reviewer approves only once every `before_merge` check has a passing result current at the head.
|
|
25
|
+
|
|
26
|
+
On your first pass over the spec, cut each requirement carried over from an earlier issue's spec or
|
|
27
|
+
decisions that no source of this issue states: those were decided for that issue. This issue's
|
|
28
|
+
sources are the words a person wrote in it, and its `Filed for` line with what that line cites: the
|
|
29
|
+
answer, Slack message or direct message of a `Filed for <person> at their request:` line, or the
|
|
30
|
+
evidence of a controller's `Filed for Legion from <role> on <issue>:` line
|
|
31
|
+
([The `Filed for` line](skill://legion-controller/references/finding-asks.md#the-filed-for-line)).
|
|
32
|
+
|
|
33
|
+
## Which gate runs
|
|
34
|
+
|
|
35
|
+
The approval step runs only when the `Design gate policy` sentence of your `Legion addressing` line
|
|
36
|
+
says `gates.design: root-issues`. When it says `gates.design: off`, write the spec and register it
|
|
37
|
+
with `register_gate` at its current version, with no approval step: do not request approval and do
|
|
38
|
+
not wait for `design-approved`; the gate opens at registration. A sub-architect on a child issue
|
|
39
|
+
has no policy line and never runs the gate.
|
|
40
|
+
|
|
41
|
+
## The armed gate
|
|
42
|
+
|
|
43
|
+
When the gate is armed, run this exact sequence before the tree's work starts:
|
|
44
|
+
|
|
45
|
+
1. Extend the primary document in place:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
dispatch doc-edit --issue <root issue> --ops-json-file - <<'EOF'
|
|
49
|
+
...
|
|
50
|
+
EOF
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
2. Settle every decision block as
|
|
54
|
+
[Approval](skill://dispatch-writing-specs/SKILL.md#approval) says;
|
|
55
|
+
`dispatch request-approval` refuses while one is open. Each answer reaches you, since you follow
|
|
56
|
+
every ask you open.
|
|
57
|
+
3. Before every approval request, give two fresh reviewers the current root spec, the code and
|
|
58
|
+
command output it relies on, and the human's words: `task(agent="plan-gap-analyst")` finds its
|
|
59
|
+
gaps, and `task(agent="plan-reviewer")`, told the document is a spec, checks each factual claim
|
|
60
|
+
against the code, the docs or a read-only command it runs. Each runs as a job of its own, so
|
|
61
|
+
`wait` for its result and read what it found before you act on it. Resolve every finding in the
|
|
62
|
+
root spec. Check with a command each claim either reviewer marks unverified: fix it when it is
|
|
63
|
+
false, or take it out.
|
|
64
|
+
4. Request approval only once the human has agreed to every point in the spec, because the human
|
|
65
|
+
approves what they already agreed to and a point new in the request has no decision block for
|
|
66
|
+
them to answer: give such a point its own decision block first, or take it out of the spec.
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
dispatch request-approval --issue <root issue> --include-verification --summary '<what the human is approving> Reviews: <gap result>, <claim result>; <what they do not show>.'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
`--include-verification` asks for the spec and the Verification document in one request, each
|
|
73
|
+
at its own current settled version, and the human's answer records a review on each. An issue
|
|
74
|
+
with no `verification.md` is refused `NO_VERIFICATION_DOCUMENT`, and one whose Verification
|
|
75
|
+
document has no settled version `NO_VERSION`.
|
|
76
|
+
|
|
77
|
+
`--summary` says in one to three sentences what the human is approving and nothing else (no
|
|
78
|
+
commentary and no open question), then one sentence with both reviews' results and what they do
|
|
79
|
+
not show, for example `Reviews: no open gap, 14 of 14 claims verified; neither shows that the
|
|
80
|
+
design works once built.` The primary document is the default; the
|
|
81
|
+
command prints `Approval requested for spec.md (document id <UUID>) at version <N> (ask <id>)`.
|
|
82
|
+
5. Register the spec's version with the daemon:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
legion({
|
|
86
|
+
op: "register_gate",
|
|
87
|
+
issue: "<root issue>",
|
|
88
|
+
artifactId: "<UUID>", // the spec's document id, which the request printed
|
|
89
|
+
version: <N>, // the version the human is asked to approve
|
|
90
|
+
})
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Copy `artifactId` and `version` from what `dispatch request-approval` printed. The document id is
|
|
94
|
+
never the slug or file name you passed in (`spec`, `spec.md`): the daemon recognizes the
|
|
95
|
+
document's approval events by that id alone; the `legion` tool looks a slug or file name up in
|
|
96
|
+
Dispatch and hands the daemon the id.
|
|
97
|
+
|
|
98
|
+
The gate records one document. A request naming the spec and the Verification document sends
|
|
99
|
+
an `artifact.approved` event for each, and the gate opens on the spec's alone, so register the
|
|
100
|
+
spec's id and version and never the Verification document's: a Verification-only version
|
|
101
|
+
closes and opens nothing. INFRA-175 is where that changes.
|
|
102
|
+
|
|
103
|
+
Facts about the request:
|
|
104
|
+
|
|
105
|
+
- `dispatch request-approval` opens a system question with the fixed options `Approve` and
|
|
106
|
+
`Request changes`; a human answers it from the Inbox or approves from the document's header.
|
|
107
|
+
Never open a `dispatch ask` with an `Approve` option yourself: the daemon ignores its answer.
|
|
108
|
+
- Repeating the request while it waits on the human, with the same `--summary`, changes nothing and
|
|
109
|
+
returns it ("already waits on the human"); a different `--summary` is refused then.
|
|
110
|
+
- A newer version of the spec moves the open request to that version and leaves it waiting on you.
|
|
111
|
+
No wake tells you when the edit was yours. Once the human has agreed to every point in it, call
|
|
112
|
+
again to hand the same request back at the latest version.
|
|
113
|
+
- If the output reads `spec.md (document id <UUID>) is already approved at version <N>`, still call
|
|
114
|
+
`register_gate` with that id and version: the daemon reads the approval from Dispatch as it
|
|
115
|
+
registers, opens the gate and delivers `design-approved` at once, and only once even when the
|
|
116
|
+
approval's own event opened the gate first. The same read covers a human who answers between
|
|
117
|
+
your two calls: a changes request on that version arrives as `design-changes-requested`, once.
|
|
118
|
+
You never approve anything yourself.
|
|
119
|
+
- `register_gate` refused 409 `VERSION_NOT_IN_DISPATCH` means the version you named is above the
|
|
120
|
+
latest version Dispatch holds for that document (the message names both), and a version Dispatch
|
|
121
|
+
never wrote is no registration: the gate is as it was. Read the document's latest version, check
|
|
122
|
+
it is the spec the human is asked to approve, and `register_gate` that version.
|
|
123
|
+
|
|
124
|
+
Then end your turn. The registration hands the spec to the human: when nothing else holds the
|
|
125
|
+
slot, the root returns its admission slot and waits on its design gate (`awaiting_person`), as a
|
|
126
|
+
root question handed to the human does ("Every turn" in `skill://legion-architect/SKILL.md`). Stay
|
|
127
|
+
live to read the human's comments and replies. Their approval or changes request delivers your next
|
|
128
|
+
task, and so does a newer spec version once Dispatch shows the request back on your turn. While the
|
|
129
|
+
root waits on a question instead, the human's approval or changes request of the spec answers that
|
|
130
|
+
wait too, and the task says how the spec stands. Release no wave until a delivered wake shows
|
|
131
|
+
`design-approved` on the root, or a task says its design gate is open: the daemon sends a root
|
|
132
|
+
waiting on a person no `design-approved` notice, and "this resume is your design-approved wake" is
|
|
133
|
+
that wake. On `design-changes-requested`: revise the spec as the human's reason asks, run step 3's
|
|
134
|
+
two reviews on the new version, resolve every finding, request approval again and end your turn. A
|
|
135
|
+
`design-changes-requested` may name a version below the latest, or arrive after another for the
|
|
136
|
+
same version: it is a request the human made that reached the daemon late, and no approval came
|
|
137
|
+
after it. Each request arrives once. The gate is as the newest review left it; still read the
|
|
138
|
+
reason, and answer it in the spec if the latest version does not already.
|
|
139
|
+
|
|
140
|
+
## After approval
|
|
141
|
+
|
|
142
|
+
Approval is pinned to the spec version: any new version closes the gate again, and no
|
|
143
|
+
`design-approved`-type wake says so; you know from your own edit, or from the `artifact.version`
|
|
144
|
+
event on your issue. The exceptions are the version a human's own answer to a decision block
|
|
145
|
+
writes when that answer is the whole change since the version they approved, and a version whose
|
|
146
|
+
text is exactly the text of the version they approved, with no decision block in it decided
|
|
147
|
+
since, such as an accidental edit undone exactly: for either, Dispatch records an `approved`
|
|
148
|
+
review in their name at the new version and emits `artifact.approved`, and the daemon reopens the
|
|
149
|
+
gate and sends `design-approved`. Undo an accidental edit of an approved root spec exactly. When
|
|
150
|
+
the spec then reads Approved at the new version, request nothing; when it does not (a block in
|
|
151
|
+
it was decided since), request approval again with steps 3 to 5.
|
|
152
|
+
|
|
153
|
+
So edit an approved root spec only when its Summary, a requirement it marks, a check of its
|
|
154
|
+
Verification document, the tree's scope, or a
|
|
155
|
+
decision a human settled in one of its decision blocks changes. Such a change is a point the human
|
|
156
|
+
has not agreed to: put the problem behind it to them as its own decision block, with its evidence,
|
|
157
|
+
at the end of the section it changes, and add nothing else to the spec. If you edit the block before
|
|
158
|
+
the human answers, the gate closes like any new version, but their answer still carries the approval
|
|
159
|
+
to the version it writes. Once they have answered:
|
|
160
|
+
|
|
161
|
+
- When the answer is the whole change, request nothing: the document's chip reads Approved at the
|
|
162
|
+
new version, its reviews dialog shows the review in their name, and the `design-approved` the
|
|
163
|
+
daemon sent opens nothing new. Carry on, and release no wave because of it.
|
|
164
|
+
- When the answer changes the Summary, a requirement, a check or the tree's scope, rewrite that text and
|
|
165
|
+
repeat steps 3 to 5 on the new version, with a one-sentence `--summary` naming what changed since
|
|
166
|
+
the earlier approval, then the reviews' sentence (the card shows that approval, `Approved at
|
|
167
|
+
version N on <date> by <who>.`, and its `Show diff` control carries the detail). The rewrite
|
|
168
|
+
closes the gate until they approve;
|
|
169
|
+
work the daemon started at the answer keeps running. Release no new wave until the `design-approved`
|
|
170
|
+
after the rewrite; work already in flight continues. Every merger's `READY` in the tree is
|
|
171
|
+
refused until a human approves the latest version.
|
|
172
|
+
|
|
173
|
+
A settled decision is the human's. A plan that would overturn one goes back to the planner with the
|
|
174
|
+
decision kept, unless the planner brings evidence the human did not weigh that would change it
|
|
175
|
+
(such as a measurement showing the settled choice cannot pass a check); then a decision
|
|
176
|
+
block names the decision and that evidence. A plan never overturns a settled decision on its own.
|
|
177
|
+
|
|
178
|
+
A design change that leaves the Summary, the requirements, the checks, the scope and the settled
|
|
179
|
+
decisions intact (a
|
|
180
|
+
planner's measurement that finds a better way to build the same outcome) goes in the plan (the
|
|
181
|
+
issue's `plan.md` document and the planner's `plan` handoff on the issue), never into the approved spec, even
|
|
182
|
+
where the spec's text describes the older design; the reviewer reads the plan beside the spec.
|
|
183
|
+
|
|
184
|
+
When a planner's `phase-finished` names a departure from the spec's design, compare it with the
|
|
185
|
+
approved spec. If the Summary, the requirements, the checks, the scope and every settled decision
|
|
186
|
+
are unchanged,
|
|
187
|
+
the plan is the record and the tree carries on; otherwise change the root spec and request
|
|
188
|
+
approval again.
|
|
189
|
+
|
|
190
|
+
Later waves and re-scoping open children toward the same requirements need no spec edit and no new
|
|
191
|
+
approval. A child issue's spec is never gated, and the child's own Verification document is
|
|
192
|
+
written by its architect.
|
|
193
|
+
|
|
194
|
+
## A decision block for a read no pod can make
|
|
195
|
+
|
|
196
|
+
A check sometimes needs evidence from a production or external system before a
|
|
197
|
+
human can approve it: a measurement, a current count, a stored record. First check whether a
|
|
198
|
+
credential this tree's pods carry reaches it: the model route every pod has, and any identity the
|
|
199
|
+
operator's deployment grants pods. Read the pod's own environment (for example `AWS_CONFIG_FILE` or
|
|
200
|
+
`AGENT_SECRETS_URL`), because a credential can exist without any skill naming it;
|
|
201
|
+
`skill://agent-secrets` says how a pod's session uses the secrets broker's client.
|
|
202
|
+
|
|
203
|
+
When the evidence is not reachable, do not have another running session read it for this tree and
|
|
204
|
+
fold the result into the spec, because that hides the gap from the next tree that hits it. Write
|
|
205
|
+
the missing capability as its own decision block to the human (which external system, which read or
|
|
206
|
+
write, why the spec needs it), and leave it open until the human resolves it.
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# The notices the daemon writes when a write of its own did not go through
|
|
2
|
+
|
|
3
|
+
Part of `skill://legion-architect`. Read the section for the notice that woke you. Each of
|
|
4
|
+
`merge-refused`, `merge-stalled`, `merge-removed` and `merge-waiting` is the daemon reporting on
|
|
5
|
+
the merge it submitted for an accepted READY, and `message-refused` on a message it was posting
|
|
6
|
+
on an issue; each reaches the architect that owns the issue. The wake-routing table, in
|
|
7
|
+
`skill://legion-architect/references/wake-routing.md`, gives each notice its move in one
|
|
8
|
+
sentence; the section here is what that sentence stands on — what the notice's `reason` names,
|
|
9
|
+
what the daemon has and has not stopped doing, and what you read before you ask a person.
|
|
10
|
+
|
|
11
|
+
In every one of the four merge notices the issue stays in `awaiting_merge`, where no worker holds
|
|
12
|
+
a phase and a backward move is refused, so what a repository rule needs is a person's to give.
|
|
13
|
+
|
|
14
|
+
## `merge-refused`
|
|
15
|
+
|
|
16
|
+
Its `reason` names the pull request, READY's head, what the daemon asked GitHub to do (merge,
|
|
17
|
+
enqueue, or enable auto-merge for it), and GitHub's refusal, which a retry would meet again
|
|
18
|
+
(squash merges disabled, a permission the implement App lacks). The daemon has stopped submitting
|
|
19
|
+
that merge; the issue stays in `awaiting_merge`. The cause is a repository setting or an App
|
|
20
|
+
permission, a person's to change: ask one with `dispatch ask`, naming the refusal and the pull
|
|
21
|
+
request at READY's head, which that person merges under the repository's rules once it is fixed.
|
|
22
|
+
The merge closes the issue. Never bypass a rule.
|
|
23
|
+
|
|
24
|
+
## `merge-stalled`
|
|
25
|
+
|
|
26
|
+
Its `reason` names the pull request, the state and head GitHub shows (closed, or another head),
|
|
27
|
+
since when GitHub has shown it, and READY's head, which the record still awaits. GitHub's last
|
|
28
|
+
change to the pull request is more than 10 minutes old and the record never heard of it, so its
|
|
29
|
+
webhook never reached the daemon: the daemon has stopped submitting that merge, and the issue
|
|
30
|
+
stays in `awaiting_merge`. Read the pull request on GitHub. Open at READY's head, GitHub has
|
|
31
|
+
caught up: a person merges it under the repository's rules, and the merge closes the issue.
|
|
32
|
+
Closed, or at another head the daemon never recorded: ask a person with `dispatch ask`, naming the
|
|
33
|
+
pull request, what GitHub shows, and READY's head. Never bypass a rule.
|
|
34
|
+
|
|
35
|
+
## `merge-removed`
|
|
36
|
+
|
|
37
|
+
Its `reason` names who took the pull request out of the merge queue or turned its auto-merge off
|
|
38
|
+
at READY's head after the daemon submitted it, or says GitHub turned it off on its own (over a
|
|
39
|
+
repository setting such as a merge method the repository blocks) and names the App that submitted
|
|
40
|
+
the merge, which GitHub names as the actor of its own disables; then what was removed, GitHub's
|
|
41
|
+
reason (`manual`, `merge_conflict`, `manually_disabled`, `merge_commit_blocked`, …), when, and
|
|
42
|
+
READY's head. It was not a failed merge, which comes as a `checks-red` instead. The daemon submits
|
|
43
|
+
that merge no further, so the removal stands, and the issue stays in `awaiting_merge`. Read the
|
|
44
|
+
pull request on GitHub. A `merge_conflict` conflicts with the base or with a pull request queued
|
|
45
|
+
ahead of it: once the conflict is on the base, the daemon's own conflict check sends the issue
|
|
46
|
+
back to `implementing` by itself. Otherwise ask a person with `dispatch ask` (the one who removed
|
|
47
|
+
it, when the reason names a person rather than GitHub on its own), naming the pull request at
|
|
48
|
+
READY's head and the reason; that person merges or queues it again under the repository's rules,
|
|
49
|
+
and the merge closes the issue. Never bypass a rule.
|
|
50
|
+
|
|
51
|
+
## `merge-waiting`
|
|
52
|
+
|
|
53
|
+
Its `reason` names the pull request, READY's head, how long the row has waited and what GitHub
|
|
54
|
+
last answered. GitHub has not completed the merge the daemon submitted within an hour of the
|
|
55
|
+
workflow queueing it at READY's acceptance. Nothing has been withdrawn: the merge stands, the
|
|
56
|
+
issue stays in `awaiting_merge`, and the daemon goes on submitting it, so there is nothing to undo
|
|
57
|
+
and you are told once, however much longer it waits. Read the pull request on GitHub and find what
|
|
58
|
+
GitHub is waiting for: a required check that never ran, a required check that failed, a review
|
|
59
|
+
only a person can give, or a merge queue that has not landed it. Act on what you find. In
|
|
60
|
+
`awaiting_merge` no worker holds a phase and a backward move is refused, so what a check, a review
|
|
61
|
+
or a repository rule needs is a person's: ask one with `dispatch ask`, naming the pull request at
|
|
62
|
+
READY's head and what GitHub waits on; the merge closes the issue when GitHub completes it,
|
|
63
|
+
whoever unblocks it. Never bypass a rule.
|
|
64
|
+
|
|
65
|
+
## `message-refused`
|
|
66
|
+
|
|
67
|
+
Its `reason` names the issue, Dispatch's own answer, and what the write carried: a message, with
|
|
68
|
+
how long it was and its first line, or a status write, with the status and whether it was set.
|
|
69
|
+
Dispatch refused that write the way it would refuse every attempt, so the daemon makes it no
|
|
70
|
+
more.
|
|
71
|
+
|
|
72
|
+
A close reason and a READY packet are checked against Dispatch's own bounds at the call that
|
|
73
|
+
carries them — `close_root` answers `CLOSE_REASON_TOO_LONG` and `handoff_complete` answers
|
|
74
|
+
`READY_PACKET_TOO_LONG`, each naming the bound and how far over the text is — so this notice is a
|
|
75
|
+
refusal those checks did not predict.
|
|
76
|
+
|
|
77
|
+
On a status write the daemon writes the status all the same, retrying it on the row's backoff
|
|
78
|
+
until Dispatch takes it, so the status is set unless Dispatch refused the status itself — the
|
|
79
|
+
reason says which. With the status set, only the reason beside it is on no issue: the issue's
|
|
80
|
+
lifecycle stands and nothing waits on you. With the status refused, the issue keeps the status
|
|
81
|
+
it had while the daemon's own record reads the one the write carried, and no retry of the
|
|
82
|
+
daemon's closes that gap: ask a person with `dispatch ask` to set on the issue the status the
|
|
83
|
+
reason names, saying what the record reads. The reason beside such a write may be on the issue
|
|
84
|
+
already, which the notice says. On a message the message is on no issue and nothing else about
|
|
85
|
+
the issue changed: no phase moved, and no worker was told.
|
|
86
|
+
|
|
87
|
+
Either way, what a person still needs is yours to post on the issue with `dispatch comment`. A
|
|
88
|
+
refused READY packet is nobody's to send again: the issue is in `awaiting_merge`, where no worker
|
|
89
|
+
holds a phase, and its merger's phase is over, so a completion from it is refused. Write that
|
|
90
|
+
comment yourself, naming the pull request at READY's head and where its verification stands; the
|
|
91
|
+
merge the daemon submits is unaffected and closes the issue when GitHub completes it.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
## Wake routing
|
|
2
|
+
|
|
3
|
+
Handle one delivered wake by verifying the relevant live artifact and then performing the
|
|
4
|
+
corresponding lifecycle procedure. Architect-addressed wakes always reach the architect that owns
|
|
5
|
+
the payload issue: a claimed child sub-architect, otherwise the nearest claimed ancestor, then the
|
|
6
|
+
root. A wake about an architect role's own launch reaches the architect above it. `phase-finished`,
|
|
7
|
+
worker lifecycle, child lifecycle, and design-gate wakes are architect-only and never go to an
|
|
8
|
+
active phase worker.
|
|
9
|
+
|
|
10
|
+
The merge-notice rows and `message-refused` are one sentence each; read
|
|
11
|
+
`skill://legion-architect/references/merge-notices.md` for the notice that woke you — what its
|
|
12
|
+
`reason` names, what the daemon has stopped doing, and what you read before you ask a person.
|
|
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
|
+
|
|
19
|
+
| Wake | Procedure |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `carried-gate-opened` | Only a root whose catch-up said `carried` gets it. Its `version` and `reason`: a human approved the old root's spec document at `version`, which opened the gate this root carried; the daemon has released any READY it refused and started any child waiting on it. It is not `design-approved`: do not proceed to section 2 or release a wave because of it, and start nothing. |
|
|
22
|
+
| `child-status` | A child of your tree left the workflow or re-entered it, or joined your tree from the admission queue; the reason names the child and its new status, or reads `<KEY> joined the tree of <ROOT> from the admission queue`. `todo` (a human's move, your `release_children`, or your `rerun_child`) means it runs again under your tree from planning, and the daemon starts it; you start nothing. A child held for a blocker starts the same way once nothing holds it, and the reason, naming it and its root, says what cleared the hold: its last blocker's close, naming the issues it waited on, now done, or a person or you taking its blocked-by links off; you start nothing for it either. `backlog`, `icebox` or `triage` (a human's move, or your `park_child`) means the daemon has suspended its workers, and it advances no further until it is set back to `todo`. Joined from the admission queue means a root that waited for a slot and never ran, which a person made a child of your tree or your `release_children` adopted, now runs in your tree with no slot or architect of its own, from planning when it is `todo` and parked otherwise; one that waits on an unfinished blocked-by issue is held for that blocker like any other child, and starts when nothing holds it; you start nothing. What the rest of the tree does is your decision. When a park leaves no child that an issue in `accepting` waits on, run that issue's last round (`skill://legion-architect/references/acceptance.md`). |
|
|
23
|
+
| `child-closed` | Read the child completion and remaining open children. Re-scope open work, park obsolete work for good with `park_child` (first take its link off each sibling that waits on it, or park that sibling too, as section 2 says), and file any new child the result calls for, after the red-team check in `skill://legion-architect/references/decomposition.md`, and release it at once with `release_children`, blocked by what it builds on (section 2). When a worker waits on this child for a missing surface (see `phase-finished`), tell it to continue with `envoy_publish` to its role topic. The last child's close is not the end-game (section 3). When it was the last child an issue in `accepting` waits on, run that issue's last round (`skill://legion-architect/references/acceptance.md`). |
|
|
24
|
+
| `child-release-stalled` | Its `reason` names a child your `release_children` released that your tree has not recorded, its parent, and why, one of three: the parent's release has not reached the daemon after three tries; or GitHub did not answer the App installation read of a repository on the child's parent chain, and the wave left the child unrecorded because a parent on that chain is not `todo`, or did not write the child `todo` because its parent is not recorded; or the daemon's recovery of such a repository, once GitHub answered, still left children unrecorded (one notice per tree lists them all, each with its parent). Read the parent's record with the `legion` tool's `read_record` (`issue` the parent); release the parent first when the `reason` says to, then run `release_children` for the child again once the parent is recorded in your tree, and tell the controller if it never is. |
|
|
25
|
+
| `child-not-recorded` | Its `reason` names a child your tree did not record and why: its repository resolves to none (neither it nor an ancestor names one, and the project has no default), or to one not mapped to the project, or to one the daemon leaves out for a missing GitHub App. The daemon said why on the child too. Set the child's repository to one of the project's (`dispatch issue-update --repo owner/name`), then release it again with `release_children`; tell the controller when the repository it needs is not served. |
|
|
26
|
+
| `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. A root that was waiting on a question in `awaiting_person` gets no such notice: the task that resumes it says "Your spec is approved at version N and the design gate is open: this resume is your design-approved wake", and that resume is this wake — proceed to section 2 on it. |
|
|
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. On a root that was waiting on a question in `awaiting_person`, a review the human gave after the wait began comes in the resume task, not in this notice, whether it answered the wait or came after something else did: the task names the review with its version and reason, unless a later approval superseded it. A `version` below the latest, or a second request for one version, is a request that reached the daemon late: read it as the same section's last paragraph says. Each request arrives once. |
|
|
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 finishing phase's handoff on the Dispatch issue (`dispatch handoff-read --issue <issue> --phase <word>`); if it shows unresolved gaps, tell the role now working the issue (`envoy_publish` to its role topic). A `planner` notice that names a departure from the spec's design defers to the full condition in "After approval" of `skill://legion-architect/references/design-gate.md`: when the approved Summary, the requirements, the checks, the scope and the settled decisions still hold, the plan is the record; otherwise change the root spec and request approval again as that section says. A `reviewer` notice whose GitHub review is `CHANGES_REQUESTED` needs nothing from you: the daemon has returned the issue to `implementing` (Dispatch `in_progress`) and started the **implementer**, whose correction goes through the tester and the reviewer again, never straight to retro. A `reviewer` notice with an `APPROVED` review means the issue is in `accepting` and the acceptance is yours (section 6): read the evidence, then `sign_off` (a parent only after its last round, "The last round" in `skill://legion-architect/references/acceptance.md`) or send it back with `request_backward_move`. A `tester` notice with `verdict: "fail"` — its handoff carries `implementerProof.verdict: "rejected"`, or a failure naming a check with no current passing result — has gone back to the **implementer** by the daemon's table; never record that check's result from another role. A worker that reports no surface reaches the changed path sends that report instead of completing its phase, so the daemon starts nothing more on that issue and the worker stays idle in its session, not suspended: build the surface in this tree: a child issue (infrastructure, tooling, or a skill) after the red-team check in `skill://legion-architect/references/decomposition.md`, or, when the red team folds it, tell the waiting worker to move the issue back to `implementing` with `request_backward_move`, naming the surface. When it is a child, tell the waiting worker to continue with `envoy_publish` to its role topic once that child's `child-closed` arrives. That report is never a reason to advance the phase. |
|
|
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: no handoff is a commit, so every such push counts except a push by the review App (a planner's, tester's, reviewer's or architect's), and the head pushed onto a red the tester's red tests earned (a planned red) does not count either. Published once per exhausted count, not on every later red verdict for that count. Read the failed CI evidence and recovery attempts. The notice moves nothing, and no later CI red within the attempt that ran the count out (the counted head, and every head pushed on top of it until a push lands on a head CI judged green or on a settled head whose base branch requires no check and no workflow, the tester's red tests onto the held red included) sends the work back from `testing`, `reviewing`, `accepting`, `retro` or `merging` either: the issue stays in its phase until you move it, or a worker's own decision does (a tester's `fail`, a reviewer's request for changes). A head pushed onto green CI, or onto a settled head whose base branch requires no check and no workflow, leaves that attempt, so its red still comes as a `checks-red`. In `implementing`, give the implementer the failing checks (`envoy_publish` to its role topic). In any later phase a worker holds, tell that worker (`envoy_publish` to its role topic) to move the issue back to `implementing` with `request_backward_move`, naming the failing checks, and the daemon starts the implementer. In `accepting`, where no worker holds a phase, the move is yours: `request_backward_move` with `issue`, naming the failing checks. In `awaiting_merge`, where no worker holds a phase and a backward move is refused, open a `dispatch ask` naming the failing checks for the human who merges. Do not treat the blocked PR as final. A `review_round_cap` reason is the implementer's round count instead: every move back to an earlier phase counts one, and the notice comes again with each round past the cap. It moves nothing either, since the round that reached the cap has already started its worker. Tell the rounds apart by what announced each: a tester's fail by its `phase-finished` with `verdict: "fail"`; a request for changes by the reviewer's `phase-finished` over a `CHANGES_REQUESTED` review; the daemon's own move by a `checks-red` notice, whose reason names a conflict (`the head conflicts with <base>`), the required checks that failed (`CI is red at <head>`), or the merge queue's failed merge and its failing run (`the merge queue took pull request #<n> out at READY's head …`); a worker's `request_backward_move` by a `phase-finished` from the role that asked, whose reason reaches only the task of the worker the move started, so ask that worker; your own send-back from `accepting` by the reason you gave. Judge the cap by the rounds that are not conflicts. Conflict returns need nothing from you beyond the next round. Tester fails, requests for changes and CI-red returns (a `checks-red` notice is raised for CI only when a required check fails on code the branch pushed, or when the merge queue fails the merge) are the rounds the cap is for. Each `review_round_cap` notice is a decision you make in the turn it wakes, never one left for the next round to make: read what every counted round found, choose one of the three below, act on it in the same turn, and post it as a `dispatch comment` on the issue naming the rounds you read and the choice. **Narrow**: the change itself is sound but each round finds more around it; tell the implementer and the reviewer (`envoy_publish` to each one's role topic) which checks of the issue the next round answers; a correctness finding is still fixed in this pull request, and so is a wording nit or any clear fix in a file it touches, so the `Fast-follow:` line carries only cleanup in files this pull request does not touch. **Split**: part of the work cannot settle in this pull request and is a separately deliverable change (`skill://legion-architect`, section 1). On a child, run the red-team check in `skill://legion-architect/references/decomposition.md`, then file that part as a child of this issue's parent, never of this one (this issue closes at its own merge, and the split part usually builds on this pull request's unmerged code): `dispatch issue --project <project> --parent <parent> --title '<outcome>' --blocked-by <this issue> --spec-file -`. Release it now with `release_children`: Legion holds it on that `--blocked-by` link until this issue closes, and starts it then. On the root, narrow it to what settles here and open a `dispatch ask` for the rest (`skill://legion-worker/references/findings.md`): a person decides whether that becomes an issue. Tell the implementer and the reviewer that this pull request no longer carries it. **Accept the rounds**: each round found less than the one before it, and the last found no defect in the change itself (a stale line, a comment that states an old default); let the loop run and say why in the comment. A later notice is a new decision: accept again only when its round found less than the round you last accepted, and otherwise narrow or split. A finding that changes what the spec requires or what a check proves is a decision block in the root spec ("After approval" in `skill://legion-architect/references/design-gate.md`), and `dispatch ask` is only for a decision that needs a human's authority; neither replaces the choice. Judge a worker's backward move by the reason that worker gives. A move you ordered for a conflict found before `awaiting_merge` ("A conflicting head" in `skill://legion-architect/references/acceptance.md`) is a conflict return. The merger's move back to `reviewing` for a READY refusal (`READY_UNREVIEWED_CHANGE`, or `READY_NOT_APPROVED` when its task names no approved head), and the reviewer's no-fix request for changes that follows it so the tester runs the head, are not the work going in circles either. A move that sends back code found wrong counts with the tester fails. |
|
|
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. |
|
|
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. |
|
|
32
|
+
| `pull-request-missing` | Its `reason` names the issue, the repository, the branch and the handoff commit: the issue's implementer completed at that commit and GitHub lists no open pull request on the branch, so the issue stays in `implementing`. Tell the implementer (`envoy_publish` to its role topic) to open the pull request from that branch; the daemon records it and moves the issue to `testing` once it is open. When the work should not go on, decide as the `pr-closed-unmerged` row says. It comes once per handoff commit. |
|
|
33
|
+
| `message-refused` | Dispatch refused a write the daemon was making on an issue of your tree, the way it would refuse every attempt, so the daemon makes it no more. A close reason and a READY packet are held to Dispatch's bounds at the call that carries them, so this is a refusal those checks did not predict. Read its `reason`: on a status write the daemon writes the status all the same, retrying it until Dispatch takes it, so the status is set unless Dispatch refused the status itself, which the reason says — with the status set, only the reason beside it is on no issue; with the status refused, the issue keeps the status it had while the daemon's own record reads the one the write carried, a disagreement no retry of the daemon's closes, so ask a person with `dispatch ask` to set that status on the issue, and the reason beside it may be posted already, which the reason says; on a message the message is on no issue and nothing else changed. Post what a person still needs on the issue yourself with `dispatch comment` — a refused READY packet is nobody's to send again, since the issue is in `awaiting_merge` where no worker holds a phase, and the merge the daemon submits is unaffected (`skill://legion-architect/references/merge-notices.md#message-refused`). |
|
|
34
|
+
| `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`). |
|
|
35
|
+
| `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`). |
|
|
36
|
+
| `merge-removed` | A person, or GitHub on its own, took the pull request out of the merge queue or turned its auto-merge off at READY's head after the daemon submitted the merge: read the pull request on GitHub and act on the removal's reason (`skill://legion-architect/references/merge-notices.md#merge-removed`). |
|
|
37
|
+
| `merge-waiting` | GitHub has not completed the merge the daemon submitted within an hour of READY's acceptance, and nothing has been withdrawn: read the pull request on GitHub for what GitHub waits on and ask a person for what no worker can do (`skill://legion-architect/references/merge-notices.md#merge-waiting`). |
|
|
38
|
+
| 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. |
|
|
39
|
+
| `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. |
|
|
40
|
+
| `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. |
|
|
41
|
+
| `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. |
|