@sjawhar/opencode-legion-envoy 9.0.0 → 9.2.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/dist/dispatch.js +162 -41
- package/dist/src/server.js +20 -17
- package/package.json +1 -1
- package/skills/AGENTS.md +7 -6
- package/skills/dispatch/SKILL.md +54 -169
- package/skills/dispatch-first/SKILL.md +23 -17
- package/skills/dispatch-writing-specs/SKILL.md +76 -0
- package/skills/leading-a-work-stream/SKILL.md +96 -0
- package/skills/leading-a-work-stream/references/daily-plan.md +8 -0
- package/skills/leading-a-work-stream/references/decision-rights.md +6 -0
- package/skills/leading-a-work-stream/references/direction-and-coaching.md +13 -0
- package/skills/leading-a-work-stream/references/dispatch-upkeep.md +7 -0
- package/skills/leading-a-work-stream/references/sources.md +9 -0
- package/skills/leading-a-work-stream/references/speed-tick.md +44 -0
- package/skills/leading-a-work-stream/references/user-quotes.md +7 -0
- package/skills/legion-architect/SKILL.md +11 -8
- package/skills/legion-architect/references/acceptance.md +18 -5
- package/skills/legion-architect/references/decomposition.md +3 -2
- package/skills/legion-architect/references/design-gate.md +42 -19
- package/skills/legion-architect/references/wake-routing.md +4 -4
- package/skills/legion-controller/SKILL.md +31 -17
- package/skills/legion-controller/references/admission.md +5 -3
- package/skills/legion-controller/references/backlog-stewardship.md +17 -2
- package/skills/legion-controller/references/daily-report.md +4 -4
- package/skills/legion-controller/references/finding-asks.md +111 -14
- package/skills/legion-controller/references/slack-mentions.md +2 -2
- package/skills/legion-controller/references/triage.md +9 -5
- package/skills/legion-controller/references/wakes.md +22 -8
- package/skills/legion-worker/SKILL.md +7 -6
- package/skills/legion-worker/references/conflicts-and-rewrites.md +23 -16
- package/skills/legion-worker/references/findings.md +126 -31
- package/skills/legion-worker/references/merge-gate.md +57 -25
- package/skills/legion-worker/references/pr-body.md +9 -5
- package/skills/legion-worker/references/skip-checks-trailer.md +1 -1
- package/skills/dispatch/references/api.md +0 -21
- package/skills/dispatch/references/asks.md +0 -43
- package/skills/dispatch/references/document-edits.md +0 -59
- package/skills/dispatch/references/documents.md +0 -79
- package/skills/dispatch/references/examples.md +0 -85
- package/skills/dispatch/references/issues.md +0 -66
- package/skills/dispatch/references/messages.md +0 -31
- package/skills/dispatch/references/reading.md +0 -32
- package/skills/dispatch-brainstorming/SKILL.md +0 -56
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Finding asks
|
|
2
2
|
|
|
3
|
-
Read this when a finding ask's reference reaches you on the controller topic,
|
|
4
|
-
is answered, when you file an issue a person asked for, and at every start.
|
|
5
|
-
and where it goes is `skill://legion-worker/references/findings.md`; what
|
|
6
|
-
skill's "Findings, and the issues you file" section.
|
|
3
|
+
Read this when a clear defect or a finding ask's reference reaches you on the controller topic,
|
|
4
|
+
when one you follow is answered, when you file an issue a person asked for, and at every start.
|
|
5
|
+
What a finding ask is and where it goes is `skill://legion-worker/references/findings.md`; what
|
|
6
|
+
you may file is the skill's "Findings, and the issues you file" section.
|
|
7
7
|
|
|
8
8
|
## The `Filed for` line
|
|
9
9
|
|
|
@@ -18,6 +18,55 @@ request: <request>`, because triage reads it to tell your filing from anyone els
|
|
|
18
18
|
id: write `direct message to session <your session id>`, for the person by the name they gave,
|
|
19
19
|
else `the person who messaged this session`.
|
|
20
20
|
|
|
21
|
+
For a clear defect no running tree owns, the line is `Filed for Legion from <role> on <issue>:
|
|
22
|
+
<evidence>` ([Hand over a clear defect](#hand-over-a-clear-defect)).
|
|
23
|
+
|
|
24
|
+
## Hand over a clear defect
|
|
25
|
+
|
|
26
|
+
A role sends a clear defect on the controller topic: its role token, the defect, the issue it came
|
|
27
|
+
from, where the fix belongs, the fix, and its evidence comment on the report issue, whose first line
|
|
28
|
+
is `Clear defect from <role>`. When the report issue was missing or closed, the message carries the
|
|
29
|
+
evidence: create or reopen the report issue (`skill://legion-controller/references/daily-report.md`)
|
|
30
|
+
and post it there with that first line. A clear defect you find yourself gets the same comment, with
|
|
31
|
+
`controller` as the role. A clear-defect ask (`findings.md`, "When no controller takes it") needs
|
|
32
|
+
no answer: take it as below, the ask its evidence. Resolve it (`dispatch resolve-ask --kind
|
|
33
|
+
resolved`) only once it carries your reply: `Handed` or `Filed` from step 2, 3 or 5, or, when
|
|
34
|
+
step 1 finds it is not clear, `Moved dispatch://<REPORT KEY>/ask/<new id>` naming the decision ask
|
|
35
|
+
you open. With no reply, leave it open: catch-up step 2 takes it again at your next start.
|
|
36
|
+
|
|
37
|
+
The work goes to whoever already owns that code. You file an issue only when nobody does.
|
|
38
|
+
|
|
39
|
+
1. Check that it is clear: the fix needs no choice a person must make, and it belongs in a
|
|
40
|
+
repository your addressing line lists. Otherwise open an ask for it, as `findings.md` says.
|
|
41
|
+
2. Search first (`skill://dispatch-first`). When an open issue tracks it, comment the evidence
|
|
42
|
+
there, reply `Filed dispatch://<that KEY>` under the evidence, and file nothing.
|
|
43
|
+
3. Find the running tree whose code it is: a root in `admission.active` whose repository and
|
|
44
|
+
component match, read from `read_state` and `dispatch read`. When one matches, hand it over.
|
|
45
|
+
`envoy_publish` the defect, its evidence reference and the fix to that tree's architect topic,
|
|
46
|
+
`notifications.role.<issues.<KEY>.architect.locator.claim>`. The architect decides whether it
|
|
47
|
+
is a child of the tree or part of an open issue's work. Reply `Handed dispatch://<that KEY>`
|
|
48
|
+
under the evidence only when the publish names a holder. When the root has no locator, or the
|
|
49
|
+
publish answers no holder, reply nothing: no architect reads a comment on its root, so the
|
|
50
|
+
evidence stays unhandled, and catch-up step 4 lists it again at your next start. Publish it
|
|
51
|
+
again at your next wake. Once the root leaves `admission.active`, no running tree owns the
|
|
52
|
+
code, and step 4 files it. File nothing while a tree owns it: a second issue splits the work.
|
|
53
|
+
4. No running tree owns that code: file it on the repository where the fix belongs, in the
|
|
54
|
+
project that repository maps to:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
dispatch issue --project <KEY> --repo <owner/name> --title '<the defect, as a title>' --label legion --spec-file -
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Without `--repo`, Dispatch records the project's default repository, and the daemon admits the
|
|
61
|
+
issue there. The spec states the defect, where the fix belongs and the fix, and ends with the
|
|
62
|
+
`Filed for Legion from` line: the sending role's token (or `controller`), the `dispatch://<KEY>`
|
|
63
|
+
it came from, and the evidence comment's or the ask's reference. It opens in `triage` with the
|
|
64
|
+
label, and triage admits it (`skill://legion-controller/references/triage.md`).
|
|
65
|
+
5. Reply under the evidence comment (`--reply-to '<comment id>'`) or the ask
|
|
66
|
+
(`--reply-to-ask '<ask id>'`, on the ask's issue), its first line exactly
|
|
67
|
+
`Filed dispatch://<NEW KEY>` or `Handed dispatch://<KEY>`. A later session reads that reply at
|
|
68
|
+
triage and at every start.
|
|
69
|
+
|
|
21
70
|
## Follow it
|
|
22
71
|
|
|
23
72
|
A finding ask another role opened reaches you as its reference, `dispatch://<KEY>/ask/<id>`, on
|
|
@@ -27,14 +76,18 @@ the controller topic. Follow it, so that its answer reaches you:
|
|
|
27
76
|
dispatch follow --ask 'dispatch://<KEY>/ask/<id>' --action follow
|
|
28
77
|
```
|
|
29
78
|
|
|
79
|
+
When `dispatch read --ref` shows it answered already, act on its answer now.
|
|
80
|
+
|
|
30
81
|
## Act on its answer
|
|
31
82
|
|
|
32
83
|
Once it is answered (an ask you follow brings its answer to you), read it with
|
|
33
84
|
`dispatch read --ref 'dispatch://<KEY>/ask/<id>'` and act on the newest answer. When its typed
|
|
34
|
-
text asks for a different one of the
|
|
35
|
-
asks; when the text asks for something that is none of the
|
|
85
|
+
text asks for a different one of the four actions than the option selected, do what the text
|
|
86
|
+
asks; when the text asks for something that is none of the four, reply under the ask with your
|
|
36
87
|
question and file nothing:
|
|
37
88
|
|
|
89
|
+
- `Hand it to the owning tree`: take it as [Hand over a clear defect](#hand-over-a-clear-defect)
|
|
90
|
+
says, from its step 3.
|
|
38
91
|
- `Do not file`: file nothing.
|
|
39
92
|
- `File an issue`: `dispatch issue --project <PROJECT> --title '<the problem, as a title>'
|
|
40
93
|
--spec-file -` in the project where the fix belongs, as the answer or the ask names it; when
|
|
@@ -48,11 +101,13 @@ question and file nothing:
|
|
|
48
101
|
An ask that re-files an escalated child also takes the steps of
|
|
49
102
|
`skill://legion-controller/references/escalation.md`.
|
|
50
103
|
|
|
51
|
-
Then reply under the ask with what you did, its first line exactly `Filed dispatch://<NEW KEY
|
|
52
|
-
`
|
|
104
|
+
Then reply under the ask with what you did, its first line exactly `Filed dispatch://<NEW KEY>`,
|
|
105
|
+
`Handed dispatch://<KEY>` or
|
|
106
|
+
`Not filed: <reason>`. The ask's issue is the report issue, or the role's own issue for an ask no
|
|
107
|
+
controller took:
|
|
53
108
|
|
|
54
109
|
```text
|
|
55
|
-
dispatch comment --issue <
|
|
110
|
+
dispatch comment --issue <the ask's issue> --reply-to-ask '<ask id>' --body-file -
|
|
56
111
|
```
|
|
57
112
|
|
|
58
113
|
Dispatch refuses to resolve an answered ask, so that reply is its record. An open finding ask that
|
|
@@ -63,16 +118,58 @@ turns moot (an issue now tracks the problem, or the problem is gone) you close w
|
|
|
63
118
|
|
|
64
119
|
An answer that arrives while no controller runs never wakes you, a reference published then
|
|
65
120
|
reaches nobody, and a new session follows none of the asks an earlier one followed. So at every
|
|
66
|
-
start, catch up on the report issue (with none yet, there is nothing to catch up)
|
|
121
|
+
start, catch up on the report issue (with none yet, there is nothing to catch up), and on the
|
|
122
|
+
finding asks roles opened on their own issues while no controller ran:
|
|
67
123
|
|
|
68
124
|
1. Read it with `dispatch read`. Reopen it when its `Status:` is `done`, and assign it when its
|
|
69
125
|
`Assignee:` says `unassigned` and a person is known, both as
|
|
70
126
|
`skill://legion-controller/references/daily-report.md` "Keep it answerable" says.
|
|
71
|
-
2.
|
|
72
|
-
|
|
127
|
+
2. Take each open ask: `dispatch open-asks --project <PROJECT>` lists every unanswered ask of the
|
|
128
|
+
project, with no cap, each line naming its issue. It leaves out an ask on a closed issue
|
|
129
|
+
(`asks.go`'s `listOpenAsks` takes `i.closed_at is null`), which is why an architect holds its
|
|
130
|
+
issue open for one. Follow those on the report issue, and take each whose question opens
|
|
131
|
+
`Clear defect from ` as [Hand over a clear defect](#hand-over-a-clear-defect) says. One on
|
|
132
|
+
another issue whose question opens `Finding from ` or `Clear defect from ` is a role's
|
|
133
|
+
own-issue ask. Take a clear defect the same way. Move a finding that needs a
|
|
134
|
+
decision: ask it again on the report issue with the same question, options and `--ref`, then
|
|
135
|
+
reply `Moved dispatch://<REPORT KEY>/ask/<new id>` under the old ask. Resolve the old ask once it
|
|
136
|
+
carries a `Moved`, `Handed` or `Filed` reply. A clear defect no holder took stays open, and the
|
|
137
|
+
next start takes it again.
|
|
73
138
|
3. List the answered asks still waiting on you, which no `dispatch` command lists:
|
|
74
|
-
|
|
75
|
-
|
|
139
|
+
|
|
140
|
+
```text
|
|
141
|
+
asks=$(curl -sS --fail-with-body -H "Authorization: Bearer $(cat "$DISPATCH_TOKEN_FILE")" "$DISPATCH_URL/api/v1/issues/<ISSUE KEY>/asks?state=answered&thread=1") || { echo "Dispatch read failed" >&2; exit 1; }
|
|
142
|
+
printf '%s' "$asks" | jq -cn 'input | if type == "array" then [.[] | select(any(.options[]; .label == "Do not file" or .label == "Admit it")) | .answer.at as $t | select([.thread.replies[]? | select(.author.kind == "session" and .created_at > $t and (.body | test("^(Filed dispatch://|Handed dispatch://|Admitted dispatch://|Not filed:|Not admitted:)")))] | length == 0) | {id, question, answer}] else error("Dispatch did not answer the list") end'
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Read it for the report issue, then for each issue in `needs_review`, whose architect holds its
|
|
146
|
+
acceptance on a finding ask's answer. Act on each as above, a triage ask as
|
|
147
|
+
`skill://legion-controller/references/triage.md` says. The read lands in `asks` first, because a
|
|
148
|
+
pipeline reports only the status of `jq`. `jq -n 'input …'` fails on an empty body, where a
|
|
149
|
+
plain `jq` would succeed on it, so exit 0 means the list was read: `[]` is nothing waiting.
|
|
150
|
+
Anything else is a failed read and admits nothing.
|
|
151
|
+
|
|
152
|
+
Take the `needs_review` issues as one snapshot before you act on any of them:
|
|
153
|
+
`dispatch issues --project <PROJECT> --status needs_review --all`. `--all` reads every match in
|
|
154
|
+
one request. Never page this list: pages by `offset` skip or repeat an issue whose status
|
|
155
|
+
changes between reads, and your reply moves issues out of `needs_review`. `No issues in
|
|
156
|
+
<PROJECT>.` means nothing waits, and a command that fails admits nothing. An issue that enters
|
|
157
|
+
`needs_review` after the snapshot is read at your next start.
|
|
158
|
+
4. List the clear defects nobody took yet: each evidence comment whose first line starts
|
|
159
|
+
`Clear defect from `, with no session reply under it that starts `Handed dispatch://`,
|
|
160
|
+
`Filed dispatch://`, `Not filed:` or `Asked dispatch://` (a role that asked instead, which step
|
|
161
|
+
2 takes):
|
|
162
|
+
|
|
163
|
+
```text
|
|
164
|
+
notes=$(curl -sS --fail-with-body -H "Authorization: Bearer $(cat "$DISPATCH_TOKEN_FILE")" "$DISPATCH_URL/api/v1/issues/<REPORT KEY>/comments") || { echo "Dispatch read failed" >&2; exit 1; }
|
|
165
|
+
printf '%s' "$notes" | jq -cn 'input | if type == "array" then (. as $all | [.[] | select(.reply_to == null and (.body | startswith("Clear defect from "))) | .id as $id | select([$all[] | select(.reply_to == $id and .author.kind == "session" and (.body | test("^(Handed dispatch://|Filed dispatch://|Not filed:|Asked dispatch://)")))] | length == 0) | {id, body}]) else error("Dispatch did not answer the list") end'
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
It reads as step 3's read does: exit 0 with `[]` is nothing waiting, anything else failed.
|
|
169
|
+
Take each as [Hand over a clear defect](#hand-over-a-clear-defect) says. One you could not hand
|
|
170
|
+
over at an earlier wake, because no architect held its tree's topic, has no reply, so this list
|
|
171
|
+
brings it back until a tree takes it or step 4 of the hand-over files it. When one is not clear,
|
|
172
|
+
reply `Not filed: <reason>` under it and open an ask for it instead.
|
|
76
173
|
|
|
77
174
|
A read that fails, and an assignment Dispatch refuses, are handled as
|
|
78
175
|
`skill://legion-controller/SKILL.md#rules` and
|
|
@@ -43,8 +43,8 @@ Every answer is one `slack_post` call:
|
|
|
43
43
|
- `channel` is the mention's `channel_id`.
|
|
44
44
|
- `thread_ts` is the mention's `thread_ts`, or its `ts` when it has none. Either keeps the reply in
|
|
45
45
|
the thread the mention opened or joined, never at the channel's top level.
|
|
46
|
-
- `text` is what you say, at most 4,000 characters, written as `skill://dispatch`'s "
|
|
47
|
-
the
|
|
46
|
+
- `text` is what you say, at most 4,000 characters, written as `skill://dispatch-writing-specs`'s "Rules for
|
|
47
|
+
the text" say. Address the author as `<@user_id>` (by `bot_name` when a bot posted the mention
|
|
48
48
|
with no `user_id`) and give every issue as its key, its title and its Dispatch link exactly as
|
|
49
49
|
`dispatch` printed it (the `Link:` line of `dispatch read`, the link on a `Created` line or a
|
|
50
50
|
search hit): it points at Dispatch's dashboard, and `slack_post` refuses a reply linking an
|
|
@@ -6,9 +6,8 @@ Read this on a `triage` wake, at every start, and when a person hands you an iss
|
|
|
6
6
|
## At every start
|
|
7
7
|
|
|
8
8
|
1. List the project's triage issues handed to Legion:
|
|
9
|
-
`dispatch issues --project <PROJECT> --status triage --label legion --
|
|
10
|
-
|
|
11
|
-
on until you have all N rows.
|
|
9
|
+
`dispatch issues --project <PROJECT> --status triage --label legion --all`. `--all` reads every
|
|
10
|
+
match in one request, so no row is missed or repeated while the list changes.
|
|
12
11
|
2. The rows show no parent, so `dispatch read` each one and follow `Links:` up through each
|
|
13
12
|
`child_of` parent. A `child_of` under `Referenced by:` is a child of this issue, not its parent.
|
|
14
13
|
3. Leave a child that has an ancestor in `admission.active` or `admission.waiting`, because that
|
|
@@ -37,13 +36,18 @@ Read this on a `triage` wake, at every start, and when a person hands you an iss
|
|
|
37
36
|
`actor` is this session (`dispatch whoami` names it) or an earlier one of yours. For a request
|
|
38
37
|
that is an ask, an earlier session is yours when its reply under that ask (`dispatch read
|
|
39
38
|
--ref`) starts `Filed dispatch://<this KEY>` after a newest answer of `File it for Legion`.
|
|
39
|
+
- Your own filing for a clear defect no tree owns hands it over too: its spec as filed holds a
|
|
40
|
+
`Filed for Legion from <role> on <issue>: <evidence>` line, and its `actor` is a session of
|
|
41
|
+
yours. An earlier session is yours when its reply under that evidence (a comment or an ask)
|
|
42
|
+
starts `Filed dispatch://<this KEY>`.
|
|
40
43
|
- Any other actor hands over nothing. Leave the issue in `triage` and ask the person on your
|
|
41
44
|
report issue whether Legion should take it, naming the issue and who labelled it, with the
|
|
42
45
|
options `Admit it` and `Leave it`. On `Admit it`, go on to step 3. Either way, reply under the
|
|
43
46
|
ask `Admitted dispatch://<KEY>` or `Not admitted: <reason>`, because the start procedure's
|
|
44
47
|
catch-up reads that reply as done.
|
|
45
|
-
3. Admit it (`set_status` to `todo`) when a person handed it over,
|
|
46
|
-
plan or the deployment instructions say to wait or put it out of
|
|
48
|
+
3. Admit it (`set_status` to `todo`) when a person or your clear-defect filing handed it over,
|
|
49
|
+
unless the request, the weekly plan or the deployment instructions say to wait or put it out of
|
|
50
|
+
scope:
|
|
47
51
|
`legion({ op: "set_status", issue: "<issue>", status: "todo" })`.
|
|
48
52
|
4. When one of those says to wait, park it with `set_status` to `backlog`, or `icebox` when the
|
|
49
53
|
request asks for a longer deferral, instead of leaving it in `triage`; the label stays. Never
|
|
@@ -16,8 +16,9 @@ the issue header. Triage it as `skill://legion-controller/references/triage.md`
|
|
|
16
16
|
|
|
17
17
|
## `slot-free on <KEY>` (`{kind: "slot-free"}`)
|
|
18
18
|
|
|
19
|
-
The daemon released `<KEY>`'s slot
|
|
20
|
-
|
|
19
|
+
The daemon released `<KEY>`'s slot and no waiting root took it: its tree finished or left the
|
|
20
|
+
workflow, or the root returned the slot to wait on a person (`awaiting_person`, which also sends
|
|
21
|
+
its own `awaiting-person` wake). Name the free slots; take nothing.
|
|
21
22
|
|
|
22
23
|
## `todo on <KEY>` (`{kind: "todo"}`)
|
|
23
24
|
|
|
@@ -50,12 +51,25 @@ take nothing for them.
|
|
|
50
51
|
|
|
51
52
|
## `unslotted on <KEY>` (`{kind: "unslotted", reason}`)
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
another session,
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
Either a root that waited for a slot was moved from `todo` to the status `reason` names, by a
|
|
55
|
+
person or by another session, or a root waiting on a person (`awaiting_person`) could not be
|
|
56
|
+
resumed — its `legion` label is gone, or a person or a session outside its tree took its Dispatch
|
|
57
|
+
claim. Either way the daemon took it out of the workflow (`phase: done`) and starts nothing for it;
|
|
58
|
+
`reason` says which. Re-admit nothing, because the move was someone else's; `dispatch read` shows
|
|
59
|
+
who claims it now. Tell the person in your summary: "<KEY> left Legion's workflow: <reason as the
|
|
60
|
+
wake gives it>; `todo` with the `legion` label queues it again". Other admission states:
|
|
61
|
+
`skill://legion-controller/references/admission.md`.
|
|
62
|
+
|
|
63
|
+
## `awaiting-person on <KEY>` (`{kind: "awaiting-person", reason}`)
|
|
64
|
+
|
|
65
|
+
A root whose architect asked a person a question, or handed its spec to a person for approval, and
|
|
66
|
+
now waits only on them: the daemon returned
|
|
67
|
+
its slot and suspends the architect after its turn, keeping its session. Read
|
|
68
|
+
`issues.<KEY>.waitingOn`. Do not park it or start anything, and take nothing for the slot it
|
|
69
|
+
returned: it is no reason to label or admit an issue ([Free
|
|
70
|
+
slots](skill://legion-controller/SKILL.md#free-slots)). The person's answer puts it at the front of
|
|
71
|
+
`admission.waiting` by itself, and the next free slot resumes its architect. A lost label or a
|
|
72
|
+
foreign Dispatch claim sends the separate `unslotted` wake.
|
|
59
73
|
|
|
60
74
|
## `held on <KEY>` (`{kind: "held", phase, role?, reason?}`)
|
|
61
75
|
|
|
@@ -19,7 +19,7 @@ You work one phase of one issue in a jj workspace every role of the issue shares
|
|
|
19
19
|
|
|
20
20
|
## Stop rules
|
|
21
21
|
|
|
22
|
-
- **A refused completion is information, not a retry loop.** Quote the answer verbatim to the architect, so "my work is lost" and "my work belongs to an ended run" read differently. `HANDOFF_STALE_GENERATION` (the issue is on a newer run
|
|
22
|
+
- **A refused completion is information, not a retry loop.** Quote the answer verbatim to the architect, so "my work is lost" and "my work belongs to an ended run" read differently. `HANDOFF_STALE_GENERATION` (the issue is on a newer run: your turn began before the daemon's task for that run reached you, and the answer names that task when it is queued) or `HANDOFF_NO_RUN` (your claim holds no task): push nothing further, end the turn without completing, and tell the architect; if the task then arrives (none may: the new run's phase can be another claim's) and its work is already committed, call `handoff_complete` again at the same commit in that task's turn, and never redo the work or write a second handoff. `HANDOFF_NOT_CURRENT_PHASE`: the issue left your phase; report, never complete again. `HANDOFF_ALREADY_RECORDED`: the first answer stands, an acceptance or one of the refusals the daemon records (`HANDOFF_NOT_CURRENT_PHASE`, `READY_REQUIRED`, `HANDOFF_NOT_NEW`, and `HANDOFF_STALE_GENERATION` only when the issue moved during the call); if you did not see it, tell the architect. A refusal given before any fact is applied (`HANDOFF_STALE_GENERATION`, `HANDOFF_NO_RUN` and READY's checks of the head among them) records nothing, so the corrected call at the same commit is applied. A reminder is not a task: after an accepted completion, a second call is refused.
|
|
23
23
|
- **After your phase completes, the workspace is read-only for you**: no commit, no `jj new`, no edit to a tracked file, in any later turn until a new task names your phase. The active phase owns it, and you stay live only to answer questions.
|
|
24
24
|
- **Never start another Legion role**: the daemon starts every phase worker.
|
|
25
25
|
- **Never end your turn blocked on a decision someone else owns.** Send a product, scope, design, lifecycle or cross-phase decision to the owning architect as your role prompt says, with the issue, phase, verified observation, what you tried and the decision needed; a human-only to-do is a `dispatch ask`. Otherwise proceed: a phase that stops silently holds its issue until someone notices.
|
|
@@ -35,7 +35,7 @@ Read each by its `skill://` link: a read by path stops at 300 lines.
|
|
|
35
35
|
| You write or edit the PR body, record or verify a proof, or run the simplify pass | `skill://legion-worker/references/pr-body.md` |
|
|
36
36
|
| Before a push, to check your commits' App identity | `skill://legion-worker/references/default-branch-check.md` |
|
|
37
37
|
| You reply on, list or resolve review threads, or judge a bot's finding | `skill://legion-worker/references/review-threads.md` |
|
|
38
|
-
| A conflict, a retarget, comparing heads after a
|
|
38
|
+
| A conflict, a need only the base meets, a retarget, comparing heads after a forward merge, or rewriting a pushed commit | `skill://legion-worker/references/conflicts-and-rewrites.md` |
|
|
39
39
|
| You write the `skip-checks: true` trailer, or expect a body edit to re-run a check | `skill://legion-worker/references/skip-checks-trailer.md` |
|
|
40
40
|
| You review or approve, build READY, or run the production check | `skill://legion-worker/references/merge-gate.md` |
|
|
41
41
|
| The issue renames a repository, package, or URL across the codebase | `skill://legion-worker/references/systematic-rename.md` |
|
|
@@ -46,18 +46,19 @@ Read each by its `skill://` link: a read by path stops at 300 lines.
|
|
|
46
46
|
## Every role pushes its own commits
|
|
47
47
|
|
|
48
48
|
- Before a push, read `jj -R "$LEGION_WORKSPACE" log -r 'ancestors(@, 5)'` (earlier phases' commits in it are expected) and run the identity check (`skill://legion-worker/references/default-branch-check.md`). Add no attribution trailer: the pane's jj adds `Omp-Session:`.
|
|
49
|
-
- A push whose commits touch only `.legion
|
|
49
|
+
- A push whose commits touch only `.legion/<issue>/` ends its head's message with `skip-checks: true`, unless it is retro's push (the implementer's retro phase, before the merger's). Retro's push never carries the line, whatever paths it touches, because it is the last push before the merge and a base that requires a check will not merge a head whose checks never ran. No other push carries it. The daemon classifies a push by its paths, never by the trailer, so a wrong trailer can stall the issue but never makes it pass.
|
|
50
50
|
- A planner whose issue moved back from implementing starts on the remote tip, `jj -R "$LEGION_WORKSPACE" new legion/<KEY>@origin`, so its handoff never sits on the implementer's unpushed commits.
|
|
51
51
|
|
|
52
52
|
## PR body, review, and the merge gate
|
|
53
53
|
|
|
54
54
|
- The implementer opens the pull request with the line `Dispatch: <KEY>` in its body, the daemon's link to the issue when the branch name is ambiguous. Later phases push to that branch and edit that pull request, never a new one.
|
|
55
|
-
- **No deferrals.** A finding that changes behaviour, hides an error, or breaks a gate is fixed in this pull request
|
|
55
|
+
- **No deferrals.** A finding that changes behaviour, hides an error, or breaks a gate is fixed in this pull request, and so is a wording nit or any clear fix in a file it touches: a later round costs a CI pass and a re-read of the whole change. The body's one `Fast-follow:` line reads "none", or names cleanup in files this pull request does not touch.
|
|
56
|
+
- **The reviewer fixes a prose-only finding itself** (wording, a count, a tense, a citation) in its own commit, pushed before its review. The exception is the round after a READY refusal. Code or behaviour goes back (`skill://legion-worker/references/merge-gate.md`).
|
|
56
57
|
- **The merge gate**, in order: the tester's evidence green → the reviewer's approval of the head → the architect's acceptance → retro → the merger's READY → the merge, which closes the issue → the implementer's production check, after the close. When the daemon accepts READY, it submits the merge of READY's head, unless the answer's note begins `MANUAL_MERGE_REQUIRED:`, where a person merges it; no role runs `gh pr merge`.
|
|
57
58
|
- Append `<!-- legion: {"session":"<session-id>","phase":"<phase>"} -->` to every pull-request comment and review you post: the daemon finds the session behind a review by it.
|
|
58
59
|
- Never write a GitHub issue, by `gh issue` or a non-GET `gh api` on `/issues` (post or edit a PR conversation comment with `gh pr comment`): Legion's issues live on Dispatch. Never run `gh config set` or `gh alias set`, and leave `GH_TOKEN`, `GITHUB_TOKEN` and `GH_HOST` empty: the daemon rewrites your gh directory in place, and a set variable outranks it.
|
|
59
|
-
- Before you report a resource unreachable, check `AGENT_SECRETS_URL` and `AGENT_SECRETS_KEY_DIR` (then `agent-secrets <SECRET> -- <command>`; sjawhar/legion `docs/kubernetes.md`, "Operator configuration") and `AWS_CONFIG_FILE` (read its profiles): a deployment can give your pod either. `agent-secrets` refuses a secret its grant lacks and names it; that refusal is final. Your `task` subagents and background jobs inherit both, and your `gh` and `git`.
|
|
60
|
+
- Before you report a resource unreachable, check `AGENT_SECRETS_URL` and `AGENT_SECRETS_KEY_DIR` (then `agent-secrets <SECRET> -- <command>`; sjawhar/legion `docs/kubernetes.md`, "Operator configuration") and `AWS_CONFIG_FILE` (read its profiles): a deployment can give your pod either. `agent-secrets` refuses a secret its grant lacks and names it; that refusal is final. Your `task` subagents and background jobs inherit both, and your `gh` and `git`. Request a secret only to use it for your issue's work, never to learn whether it exists. A request for a secret your role does not hold reaches a person's Inbox.
|
|
60
61
|
|
|
61
62
|
## Deployment instructions
|
|
62
63
|
|
|
63
|
-
Deployment instructions in your prompt override this skill where they conflict, except four rules: no deferrals; bringing the base into the branch only on a real conflict or a retarget (`skill://legion-worker/references/conflicts-and-rewrites.md#reintegrating-the-base`); the implementer's own proof on a production-like surface at the head that merges, an applied simplify head included (`skill://legion-worker/references/pr-body.md#what-a-proof-is`, `skill://legion-worker/references/pr-body.md#the-rules-every-phases-evidence-follows`); and the implementer's production check after the close (`skill://legion-worker/references/merge-gate.md#the-production-check-after-the-close`).
|
|
64
|
+
Deployment instructions in your prompt override this skill where they conflict, except four rules: no deferrals; bringing the base into the branch only on a real conflict, a need only the base meets, or a retarget (`skill://legion-worker/references/conflicts-and-rewrites.md#reintegrating-the-base`); the implementer's own proof on a production-like surface at the head that merges, an applied simplify head included (`skill://legion-worker/references/pr-body.md#what-a-proof-is`, `skill://legion-worker/references/pr-body.md#the-rules-every-phases-evidence-follows`); and the implementer's production check after the close (`skill://legion-worker/references/merge-gate.md#the-production-check-after-the-close`).
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
# Conflicts, retargets, fingerprints, and rewriting pushed commits
|
|
2
2
|
|
|
3
3
|
Part of `skill://legion-worker`. Read it when GitHub reports the pull request `CONFLICTING`, the
|
|
4
|
-
controller asks you to resolve a conflict, the
|
|
5
|
-
compare two heads after such a merge, or you are about to
|
|
4
|
+
controller asks you to resolve a conflict, the change needs something only the base has, the pull
|
|
5
|
+
request is retargeted to a new base, you compare two heads after such a merge, or you are about to
|
|
6
|
+
rewrite a commit you already pushed.
|
|
6
7
|
Every path it cites is in sjawhar/legion.
|
|
7
8
|
|
|
8
9
|
## Reintegrating the base
|
|
9
10
|
|
|
10
|
-
- **
|
|
11
|
-
|
|
11
|
+
- **Merge the base forward only on a real conflict, a need only the base meets, or a retarget, and
|
|
12
|
+
never with `jj rebase`.**
|
|
12
13
|
The implementer merges the base into the issue branch only when GitHub reports it `CONFLICTING`, the controller asks
|
|
13
|
-
because of a conflict,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
the merge
|
|
14
|
+
because of a conflict, the change needs something that exists only on the base (a fix for its
|
|
15
|
+
only red check, or a sentence it must change), or after the pull request is retargeted to a new
|
|
16
|
+
base. Otherwise, never reintegrate the base to pick up the default branch or refresh CI. Each
|
|
17
|
+
merge is a new head, reruns CI and can void a review. A single failed CI job is re-run on its
|
|
18
|
+
own (*Never waiting on CI* in `skill://legion-worker/references/pr-body.md`). After any forward merge, build the branch and
|
|
19
|
+
run its proof again at the merge head, even with no text conflict: a clean merge once changed a
|
|
20
|
+
function's arity under the branch's own callers. Before you race another tree's pull request on
|
|
21
|
+
shared files, `envoy_publish` to its implementer's role topic, ask when its next push lands, and
|
|
22
|
+
merge once, after it. A forward merge that leaves the branch's diff unchanged is a
|
|
23
|
+
confirmation, not a new round (see *The unchanged-diff check* below). The check compares the
|
|
24
|
+
pull request's own lines file by file: a changed file sends the head through the tester again,
|
|
25
|
+
and your comment names it. Before merging, record
|
|
19
26
|
the fingerprint at the current tip; after pushing the merged branch, record it at the new
|
|
20
27
|
tip; post one PR comment (Legion footer):
|
|
21
|
-
`
|
|
28
|
+
`forward merge <old-tip-sha> → <new-tip-sha>; fingerprint <before> → <after>; unchanged|changed`.
|
|
22
29
|
On the tmux runtime, where issue workspaces are still jj workspaces of one shared clone,
|
|
23
30
|
`jj undo`, `jj abandon` and `jj op restore` rewrite the shared operation log, so a mistake there
|
|
24
31
|
is recovered forward (`jj -R "$LEGION_WORKSPACE" new`, or `jj -R "$LEGION_WORKSPACE" restore
|
|
@@ -57,7 +64,7 @@ Every path it cites is in sjawhar/legion.
|
|
|
57
64
|
|
|
58
65
|
## The unchanged-diff check
|
|
59
66
|
|
|
60
|
-
The fingerprint every role compares after a
|
|
67
|
+
The fingerprint every role compares after a forward merge (every flag and the fileset
|
|
61
68
|
verified on jj 0.45.1):
|
|
62
69
|
|
|
63
70
|
```bash
|
|
@@ -70,16 +77,16 @@ fingerprint=$(set -o pipefail; jj -R "$LEGION_WORKSPACE" diff --from "fork_point
|
|
|
70
77
|
|
|
71
78
|
- `<head-sha>` is a full commit SHA; a jj commit id is the git SHA GitHub shows.
|
|
72
79
|
- `fork_point(${base}@origin | <head-sha>)` is the base the branch was cut from *at that head*:
|
|
73
|
-
the old base for the
|
|
80
|
+
the old base for the head before the merge, the new base for the merge head, so one command serves
|
|
74
81
|
both sides. `base` is the pull request's base branch as a jj string literal, as in the merge
|
|
75
82
|
above.
|
|
76
|
-
- A head the
|
|
83
|
+
- A head the merge replaced is still addressable by its SHA in the shared workspace. The check
|
|
77
84
|
prints a fingerprint only when every step of it succeeded: the base read, the fetch and the
|
|
78
85
|
diff (`pipefail` keeps the diff's failure through `sed` and `sha256sum`, which would otherwise
|
|
79
86
|
hash empty input to `e3b0c442…`). A nonzero exit or no printed fingerprint (a failed read of the
|
|
80
87
|
base, or a SHA the workspace cannot resolve) counts as a changed diff — never as unchanged.
|
|
81
88
|
- `--context 0` drops context lines; the `sed` drops `@@` hunk headers (line positions move
|
|
82
|
-
on a
|
|
89
|
+
on a merge) and `index` lines (blob ids move when the base's copy of a file changed). What
|
|
83
90
|
is left is exactly the added and removed lines per file.
|
|
84
91
|
- The single fileset `'~(.legion | docs/solutions)'` leaves out the handoff ledger and retro's
|
|
85
92
|
learnings: process artifacts the merge gate already exempts from re-review
|
|
@@ -91,7 +98,7 @@ fingerprint=$(set -o pipefail; jj -R "$LEGION_WORKSPACE" diff --from "fork_point
|
|
|
91
98
|
`.legion/` is gone, jj warns `No matching entries for paths: .legion` on stderr; the hash is
|
|
92
99
|
unaffected.
|
|
93
100
|
|
|
94
|
-
Where each role gets its two heads: the implementer — the tip before and after its own
|
|
101
|
+
Where each role gets its two heads: the implementer — the tip before and after its own forward merge;
|
|
95
102
|
the tester — the head its `E2E` line names and the new head; the reviewer — the `commit_id` of
|
|
96
103
|
its last submitted review (`gh api --paginate repos/{owner}/{repo}/pulls/{n}/reviews --jq '.[] | {commit_id, state, user: .user.login, body}'`, whose pages concatenate in order, so the last line is the last submitted review, and whose body tells a no-fix request that answered a READY refusal from any other)
|
|
97
104
|
and the new head; the merger never computes a fingerprint — it quotes the `--summary` lines
|