@sjawhar/opencode-legion-envoy 9.0.0 → 9.1.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 +35 -27
- package/dist/src/server.js +16 -15
- 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 +4 -2
- package/skills/legion-architect/references/acceptance.md +11 -3
- package/skills/legion-architect/references/design-gate.md +32 -17
- package/skills/legion-architect/references/wake-routing.md +2 -2
- package/skills/legion-controller/SKILL.md +27 -16
- 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-worker/SKILL.md +5 -4
- 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 +48 -17
- package/skills/legion-worker/references/pr-body.md +9 -5
- 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
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# The speed tick: the 3-hourly loop and a reminder that survives restarts
|
|
2
|
+
|
|
3
|
+
## The loop, on each tick
|
|
4
|
+
|
|
5
|
+
1. Read the stream's state from its records: the numbers, today's plan against what merged, the hourly Dispatch check.
|
|
6
|
+
2. Ask each agent on Envoy what slows it down most right now. Not status.
|
|
7
|
+
3. Answer in writing: how can I help this go faster while the targets and the checks that gate them stay unchanged; how can I do better work faster; how can I help the team do better work faster?
|
|
8
|
+
4. Give the answers to a fresh subagent to attack — is each target ambitious, is each change concrete, is each date padded — and rewrite what it finds weak, because your own answers read better to you than they are.
|
|
9
|
+
5. Make the change now, or start an agent on it.
|
|
10
|
+
6. Append the tick to the stream's speed log (an artifact `speed-log.md` on the stream issue): the time, the change, the number it should move and by how much; on the next tick, what it did.
|
|
11
|
+
|
|
12
|
+
## Arming the reminder
|
|
13
|
+
|
|
14
|
+
A reminder inside the session (`sleep` in a background job) dies with the session's processes. A systemd user timer survives them and wakes the session with an Envoy message. Arm it once, from a shell on the machine the session runs on.
|
|
15
|
+
|
|
16
|
+
Replace `NAME` with a short name for your stream and `SESSION` with your full session id. The timer below fires at 01, 04, 07, 10, 13, 16, 19 and 22 UTC; change `OnCalendar` for another rhythm.
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
mkdir -p ~/.config/systemd/user
|
|
20
|
+
cat > ~/.config/systemd/user/speed-tick-NAME.service <<'EOF'
|
|
21
|
+
[Unit]
|
|
22
|
+
Description=Speed tick for NAME's lead
|
|
23
|
+
|
|
24
|
+
[Service]
|
|
25
|
+
Type=oneshot
|
|
26
|
+
ExecStart=<envoy-send-command> SESSION "SPEED TICK: run the six-step loop in leading-a-work-stream references/speed-tick.md now."
|
|
27
|
+
EOF
|
|
28
|
+
cat > ~/.config/systemd/user/speed-tick-NAME.timer <<'EOF'
|
|
29
|
+
[Unit]
|
|
30
|
+
Description=Speed tick for NAME's lead, every 3 hours
|
|
31
|
+
|
|
32
|
+
[Timer]
|
|
33
|
+
OnCalendar=*-*-* 01/3:00:00
|
|
34
|
+
Persistent=true
|
|
35
|
+
|
|
36
|
+
[Install]
|
|
37
|
+
WantedBy=timers.target
|
|
38
|
+
EOF
|
|
39
|
+
systemctl --user daemon-reload
|
|
40
|
+
systemctl --user enable --now speed-tick-NAME.timer
|
|
41
|
+
systemctl --user list-timers speed-tick-NAME.timer
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Set `ExecStart` to the Envoy send command the deployment provides. A session that runs where it cannot install a user timer (a pod) arms an equivalent wake-up there, and says in its speed log how.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# The record of the user's own words
|
|
2
|
+
|
|
3
|
+
Keep one record of the words of the person the stream works for, each quoted with its date and source.
|
|
4
|
+
|
|
5
|
+
- A skill, brief, spec or message that says that person wants something links the quote. Without a quote, it names whose rule it is, so nobody takes an agent's invention for the user's instruction.
|
|
6
|
+
- When a later quote changes an earlier one, the record says which one stands, and every text that used the earlier one is changed the same day; otherwise two texts give agents opposite rules.
|
|
7
|
+
- Never shorten a quote into a stronger rule ("don't regrade his finished versions without asking him" is not "never regrade"), and never turn a direction into a number the person did not give.
|
|
@@ -15,7 +15,8 @@ You own one issue tree from its first decision to the merge that closes it. The
|
|
|
15
15
|
- Check each claim against the code as fetched now (the default branch your addressing line names, or the head under review), never the workspace as it was at admission, and tell every fresh reader you dispatch to do the same: the branch may have moved since.
|
|
16
16
|
- Before you end a turn that waits on a human, open what they must answer: a decision block, a `dispatch ask` or `dispatch request-approval`. A turn that ends with nothing open asks nobody, and the tree stops.
|
|
17
17
|
- Deliver every criterion of the root spec in this tree. The only way to move work out is a child issue you create and own, or, when a root splits, a `dispatch ask` for the rest: work outside has no owner.
|
|
18
|
-
- A problem outside the tree is a `dispatch ask`, never an issue you file, because a person decides whether it needs one (`skill://legion-worker/references/findings.md`).
|
|
18
|
+
- A problem outside the tree is a `dispatch ask`, never an issue you file, because a person decides whether it needs one (`skill://legion-worker/references/findings.md`). A clear defect is the exception. Fix one in code this tree touches or depends on in this tree's pull request. Send any other to the controller, which gives it to the tree that owns that code.
|
|
19
|
+
- Before you write a spec or ask a person anything, read the repository's `STRATEGY.md` when it has one, then use a standard best-practice solution; ask only what neither settles, with one recommendation. A design that crosses a Boundary in that file goes to its owner before it is built. Find the standard with a web search and with how two or three well-regarded projects solve the problem. Cite them in a few lines of the spec's `Design`. A design far from the standard is a sign to fix the underlying problem, not to add a workaround.
|
|
19
20
|
- Write lifecycle changes with the `legion` tool, which takes Dispatch issue keys (`LEGION-41`). Never apply a label: Legion has none.
|
|
20
21
|
- Message a phase worker with `envoy_publish` to `notifications.role.<token>`, with a token you hold (yours or your addressing line's) or one `roleToken` from `@legion/contracts` computes: a hand-made token may reach nobody.
|
|
21
22
|
- Deployment instructions override this skill where they conflict: they are the operator's rules (required checks, deploy commands, code owners, standing roles you may consult, the merge credential).
|
|
@@ -59,7 +60,7 @@ The reviewer's `phase-finished` with an `APPROVED` review puts the issue in `acc
|
|
|
59
60
|
- the reviewer's approval of that head by SHA (`gh api --paginate repos/<o>/<r>/pulls/<n>/reviews`);
|
|
60
61
|
- each of the reviewer's prose findings, checked against the code at that head and command output, not against the reviewer's own verdict: no other agent has read that prose.
|
|
61
62
|
|
|
62
|
-
Accept only when each acceptance line has passing evidence at the approved head, every corrective child and (for a root) the integration child has closed, and no criterion left the tree. Post that evidence as a `dispatch comment`, then `legion({ op: "sign_off", issue: "<KEY>" })`.
|
|
63
|
+
Accept only when each acceptance line has passing evidence at the approved head, every corrective child and (for a root) the integration child has closed, and no criterion left the tree. A finding ask on the issue holds the acceptance until it is resolved or carries the controller's reply (`skill://legion-architect/references/acceptance.md`). Post that evidence as a `dispatch comment`, then `legion({ op: "sign_off", issue: "<KEY>" })`.
|
|
63
64
|
|
|
64
65
|
- `sign_off` works only in `accepting` (`SIGNOFF_OUTSIDE_ACCEPTING`) and is refused 409 `SIGNOFF_CHILD_IN_WORKFLOW` while any issue beneath this one is in the workflow, naming each: wait for the last, or take one out with `park_child`.
|
|
65
66
|
- When an acceptance check fails: `request_backward_move` with `issue`, `to` (`implementing` or `testing`) and a `reason` naming the failure. It counts a review round.
|
|
@@ -77,4 +78,5 @@ Handle each delivered wake by its row in `skill://legion-architect/references/wa
|
|
|
77
78
|
|
|
78
79
|
- `envoy_publish` to the controller (its topic is on your addressing line) a child that serves no criterion of the root, a need for admission slots, and two trees changing the same code, because only it sees other trees. Handle everything else.
|
|
79
80
|
- A decision that needs the human, yours or a worker's, is a decision block you write, never the worker, because a new version of an approved root spec closes the gate.
|
|
81
|
+
- A clear defect the controller hands you is in code your tree owns, so it is work inside the tree. Add it to an open child whose pull request touches that code. File a child (`dispatch issue --parent`) only when none does. Reply under its evidence comment with the issue that carries it. Change no root spec for it: a child's spec is never gated, and a new root-spec version closes the gate (`skill://legion-architect/references/design-gate.md`, "After approval"). Only a fix that would change the root's Summary, Acceptance or scope is a decision block instead.
|
|
80
82
|
- A tool a worker cannot get is yours: a pull request changing `sjawhar/legion`'s worker image (`skill://legion-worker/references/tools.md`).
|
|
@@ -33,6 +33,13 @@ legion({ op: "sign_off", issue: "LEGION-40" })
|
|
|
33
33
|
- `handoff_complete` from you in `accepting` is refused `ACCEPTANCE_IS_SIGN_OFF`. CI red at the head
|
|
34
34
|
in `accepting` returns the issue to `implementing` by the daemon's table.
|
|
35
35
|
- No `Production:` line is asked here: the production check is the implementer's, after the close.
|
|
36
|
+
- A finding ask on the issue holds the acceptance: one a worker sent you, or one `dispatch read`
|
|
37
|
+
lists under `Open asks:` whose question opens `Finding from ` or `Clear defect from `
|
|
38
|
+
(`skill://legion-worker/references/findings.md`, "When no controller takes it"). Follow each
|
|
39
|
+
(`dispatch follow --ask <id> --action follow`) and, once it is answered, publish its reference
|
|
40
|
+
to the controller topic. Accept only once each is resolved, or carries the controller's
|
|
41
|
+
`Filed dispatch://`, `Handed dispatch://` or `Not filed:` reply, because a closed issue hides its
|
|
42
|
+
asks from the controller.
|
|
36
43
|
|
|
37
44
|
## The order after your acceptance
|
|
38
45
|
|
|
@@ -128,9 +135,10 @@ The merge closes the issue: when GitHub reports the pull request merged while th
|
|
|
128
135
|
every member's open production check has closed.
|
|
129
136
|
|
|
130
137
|
Nothing of the production check is yours. Its record lives on the pull request alone, since
|
|
131
|
-
Dispatch refuses a write on a closed issue
|
|
132
|
-
|
|
133
|
-
|
|
138
|
+
Dispatch refuses a write on a closed issue. A defect it finds is never an issue the implementer
|
|
139
|
+
files: a clear defect goes to the controller, and any other is a `dispatch ask`. `read_record`
|
|
140
|
+
shows a closed issue's open check (`check`: the pull request, the merge commit, the looks, what
|
|
141
|
+
woke the last one and when the next is due).
|
|
134
142
|
|
|
135
143
|
## Hand-over at a root's linger
|
|
136
144
|
|
|
@@ -6,13 +6,13 @@ edit to an approved root spec, and on `design-approved`, `design-changes-request
|
|
|
6
6
|
|
|
7
7
|
## Writing the root spec
|
|
8
8
|
|
|
9
|
-
Specifications written into Dispatch follow
|
|
10
|
-
[
|
|
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
11
|
own status are visible from the issue tree and the handoffs; do not narrate them into the spec or a
|
|
12
12
|
`dispatch message`.
|
|
13
13
|
|
|
14
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#
|
|
15
|
+
what the human decides, each as a [decision block](skill://dispatch/SKILL.md#asks-and-ask-blocks).
|
|
16
16
|
Never post a second "spec" artifact beside it: `dispatch artifact` with the primary document's name
|
|
17
17
|
replaces the human's document.
|
|
18
18
|
|
|
@@ -37,22 +37,27 @@ When the gate is armed, run this exact sequence before the tree's work starts:
|
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
2. Settle every decision block as
|
|
40
|
-
[Approval
|
|
40
|
+
[Approval](skill://dispatch-writing-specs/SKILL.md#approval) says;
|
|
41
41
|
`dispatch request-approval` refuses while one is open. Each answer reaches you, since you follow
|
|
42
42
|
every ask you open.
|
|
43
|
-
3.
|
|
44
|
-
relies on, and the human's words
|
|
43
|
+
3. Before every approval request, give two fresh reviewers the current root spec, the code and
|
|
44
|
+
command output it relies on, and the human's words: `task(agent="plan-gap-analyst")` finds its
|
|
45
|
+
gaps, and `task(agent="plan-reviewer")`, told the document is a spec, checks each factual claim
|
|
46
|
+
against the code, the docs or a read-only command it runs. Resolve every finding in the root
|
|
47
|
+
spec. Check with a command each claim either reviewer marks unverified: fix it when it is
|
|
48
|
+
false, or take it out.
|
|
45
49
|
4. Request approval only once the human has agreed to every point in the spec, because the human
|
|
46
50
|
approves what they already agreed to and a point new in the request has no decision block for
|
|
47
51
|
them to answer: give such a point its own decision block first, or take it out of the spec.
|
|
48
52
|
|
|
49
53
|
```bash
|
|
50
|
-
dispatch request-approval --issue <root issue> --summary '<what the human is approving>'
|
|
54
|
+
dispatch request-approval --issue <root issue> --summary '<what the human is approving> Reviews: <gap result>, <claim result>.'
|
|
51
55
|
```
|
|
52
56
|
|
|
53
|
-
`--summary` says in one to three sentences what the human is approving and nothing else
|
|
54
|
-
commentary and no open question
|
|
55
|
-
`
|
|
57
|
+
`--summary` says in one to three sentences what the human is approving and nothing else (no
|
|
58
|
+
commentary and no open question), then one sentence with both reviews' results, for example
|
|
59
|
+
`Reviews: no open gap, 14 of 14 claims verified.` The primary document is the default; the
|
|
60
|
+
command prints `Approval requested for spec.md (document id <UUID>) at version <N> (ask <id>)`.
|
|
56
61
|
5. Register that document version with the daemon:
|
|
57
62
|
|
|
58
63
|
```ts
|
|
@@ -81,13 +86,22 @@ Facts about the request:
|
|
|
81
86
|
again to hand the same request back at the latest version.
|
|
82
87
|
- If the output reads `spec.md (document id <UUID>) is already approved at version <N>`, still call
|
|
83
88
|
`register_gate` with that id and version: the daemon reads the approval from Dispatch as it
|
|
84
|
-
registers, opens the gate and delivers `design-approved` at once
|
|
85
|
-
|
|
89
|
+
registers, opens the gate and delivers `design-approved` at once, and only once even when the
|
|
90
|
+
approval's own event opened the gate first. The same read covers a human who answers between
|
|
91
|
+
your two calls: a changes request on that version arrives as `design-changes-requested`, once.
|
|
92
|
+
You never approve anything yourself.
|
|
93
|
+
- `register_gate` refused 409 `VERSION_NOT_IN_DISPATCH` means the version you named is above the
|
|
94
|
+
latest version Dispatch holds for that document (the message names both), and a version Dispatch
|
|
95
|
+
never wrote is no registration: the gate is as it was. Read the document's latest version, check
|
|
96
|
+
it is the spec the human is asked to approve, and `register_gate` that version.
|
|
86
97
|
|
|
87
98
|
Then end your turn. Release no wave until a delivered wake shows `design-approved` on the root. On
|
|
88
|
-
`design-changes-requested`: revise the spec as the human's reason asks, run
|
|
89
|
-
|
|
90
|
-
|
|
99
|
+
`design-changes-requested`: revise the spec as the human's reason asks, run step 3's two reviews on
|
|
100
|
+
the new version, resolve every finding, request approval again and end your turn. A
|
|
101
|
+
`design-changes-requested` may name a version below the latest, or arrive after another for the
|
|
102
|
+
same version: it is a request the human made that reached the daemon late, and no approval came
|
|
103
|
+
after it. Each request arrives once. The gate is as the newest review left it; still read the
|
|
104
|
+
reason, and answer it in the spec if the latest version does not already.
|
|
91
105
|
|
|
92
106
|
## After approval
|
|
93
107
|
|
|
@@ -110,8 +124,9 @@ to the version it writes. Once they have answered:
|
|
|
110
124
|
daemon sent opens nothing new. Carry on, and release no wave because of it.
|
|
111
125
|
- When the answer changes the Summary, the Acceptance or the tree's scope, rewrite that text and
|
|
112
126
|
repeat steps 3 to 5 on the new version, with a one-sentence `--summary` naming what changed since
|
|
113
|
-
the earlier approval (the card shows that approval, `Approved at
|
|
114
|
-
and its `Show diff` control carries the detail). The rewrite
|
|
127
|
+
the earlier approval, then the reviews' sentence (the card shows that approval, `Approved at
|
|
128
|
+
version N on <date> by <who>.`, and its `Show diff` control carries the detail). The rewrite
|
|
129
|
+
closes the gate until they approve;
|
|
115
130
|
work the daemon started at the answer keeps running. Release no new wave until the `design-approved`
|
|
116
131
|
after the rewrite; work already in flight continues. Every merger's `READY` in the tree is
|
|
117
132
|
refused until a human approves the latest version.
|
|
@@ -24,9 +24,9 @@ and 7 (a conflict, the hand-over, the close) is in
|
|
|
24
24
|
| `child-release-stalled` | Its `reason` names a child your `release_children` released while Dispatch already showed it `todo`, and that child's parent, whose release has not reached the daemon after three tries. Read the parent's record with the `legion` tool's `read_record` (`issue` the parent); run `release_children` for the child again once the parent is recorded in your tree, and tell the controller if it never is. |
|
|
25
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
26
|
| `design-approved` | Its `version` is the approved one. A human approved the root spec document at its current version; the gate is open. Proceed to section 2 — except on a root whose catch-up said `handed_over`: its workers already run their phases, so start nothing; the approval releases any READY the gate refused. One whose `version` is the version a human's own answer to a decision block wrote, after an earlier approval (Dispatch carried that approval to it, "After approval" in `skill://legion-architect/references/design-gate.md`), opens nothing new: carry on, and release no wave because of it. |
|
|
27
|
-
| `design-changes-requested` | Its `version` and `reason`. A human asked for changes to the root spec at `version`, for `reason`. Revise the spec as the reason asks and request approval again as "The armed gate" in `skill://legion-architect/references/design-gate.md` says; end your turn; the gate is closed. |
|
|
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. 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
28
|
| `phase-finished` | The daemon has already moved the issue to its next phase by its fixed table and started that phase's role; you start nothing. Read the committed handoff for the finishing phase; if it shows unresolved gaps, tell the role now working the issue (`envoy_publish` to its role topic). A `planner` notice that names a departure from the spec's design defers to the full condition in "After approval" of `skill://legion-architect/references/design-gate.md`: when the approved Summary, Acceptance, scope and settled decisions still hold, the plan is the record; otherwise change the root spec and request approval again as that section says. A `reviewer` notice whose GitHub review is `CHANGES_REQUESTED` needs nothing from you: the daemon has returned the issue to `implementing` (Dispatch `in_progress`) and started the **implementer**, whose correction goes through the tester and the reviewer again, never straight to retro. A `reviewer` notice with an `APPROVED` review means the issue is in `accepting` and the acceptance is yours (section 6): read the evidence, then `sign_off` or send it back with `request_backward_move`. A `tester` notice with `verdict: "fail"` — its handoff carries `implementerProof.verdict: "rejected"`, or a failure naming the production-like proof — has gone back to the **implementer** by the daemon's table; never supply the proof from another role. A worker that reports no surface reaches the changed path sends that report instead of completing its phase, so the daemon starts nothing more on that issue and the worker stays idle in its session, not suspended: file a child issue in this tree to build the surface (infrastructure, tooling, or a skill), and when that child's `child-closed` arrives, tell the waiting worker to continue with `envoy_publish` to its role topic. That report is never a reason to advance the phase. |
|
|
29
|
-
| `pr-blocked` | Its `reason` names the pull request and the `max_fix_attempts` it reached, or reads `Issue reached review_round_cap=<N>.` (the end of this row). The count is of heads pushed onto a red verdict that changed something outside `.legion/` — handoff-only pushes (`.legion/` paths only) never count, a push by the review App (a planner's, tester's, reviewer's or architect's) never counts, and the head after a red the tester's red tests earned (a review-App push that changed a path outside `.legion/`, however many handoff-only pushes follow it) does not count either — so after the tester's handoff-only push onto the implementer's red, the implementer's next push does count; a push the daemon cannot classify (a listener without `changed_paths`, a list the listener stopped at 100 paths or 32,768 runes of text, a push listing no commits) does. Published once per exhausted count, not on every later red verdict for that count. Read the failed CI evidence and recovery attempts. The notice moves nothing, and no later CI red within the attempt that ran the count out (the counted head, and every head pushed on top of it until a push lands on a head CI judged green or on a settled head whose base branch requires no check and no workflow, the tester's red tests and handoff onto the held red included) sends the work back from `testing`, `reviewing`, `accepting`, `retro` or `merging` either: the issue stays in its phase until you move it, or a worker's own decision does (a tester's `fail`, a reviewer's request for changes). A head pushed onto green CI, or onto a settled head whose base branch requires no check and no workflow, leaves that attempt, so its red still comes as a `checks-red`. In `implementing`, give the implementer the failing checks (`envoy_publish` to its role topic). In any later phase a worker holds, tell that worker (`envoy_publish` to its role topic) to move the issue back to `implementing` with `request_backward_move`, naming the failing checks, and the daemon starts the implementer; or file a corrective child. In `accepting`, where no worker holds a phase, the move is yours: `request_backward_move` with `issue`, naming the failing checks. In `awaiting_merge`, where no worker holds a phase and a backward move is refused, open a `dispatch ask` naming the failing checks for the human who merges. Do not treat the blocked PR as final. A `review_round_cap` reason is the implementer's round count instead: every move back to an earlier phase counts one, and the notice comes again with each round past the cap. It moves nothing either, since the round that reached the cap has already started its worker. Tell the rounds apart by what announced each: a tester's fail by its `phase-finished` with `verdict: "fail"`; a request for changes by the reviewer's `phase-finished` over a `CHANGES_REQUESTED` review; the daemon's own move by a `checks-red` notice, whose reason names a conflict (`the head conflicts with <base>`), the required checks that failed (`CI is red at <head>`), or the merge queue's failed merge and its failing run (`the merge queue took pull request #<n> out at READY's head …`); a worker's `request_backward_move` by a `phase-finished` from the role that asked, whose reason reaches only the task of the worker the move started, so ask that worker; your own send-back from `accepting` by the reason you gave. Judge the cap by the rounds that are not conflicts. Conflict returns need nothing from you beyond the next round. Tester fails, requests for changes and CI-red returns (a `checks-red` notice is raised for CI only when a required check fails on code the branch pushed, or when the merge queue fails the merge) are the rounds the cap is for. Each `review_round_cap` notice is a decision you make in the turn it wakes, never one left for the next round to make: read what every counted round found, choose one of the three below, act on it in the same turn, and post it as a `dispatch comment` on the issue naming the rounds you read and the choice. **Narrow**: the change itself is sound but each round finds more around it; tell the implementer and the reviewer (`envoy_publish` to each one's role topic) which acceptance lines the next round answers; a correctness finding is still fixed in this pull request, and
|
|
29
|
+
| `pr-blocked` | Its `reason` names the pull request and the `max_fix_attempts` it reached, or reads `Issue reached review_round_cap=<N>.` (the end of this row). The count is of heads pushed onto a red verdict that changed something outside `.legion/` — handoff-only pushes (`.legion/` paths only) never count, a push by the review App (a planner's, tester's, reviewer's or architect's) never counts, and the head after a red the tester's red tests earned (a review-App push that changed a path outside `.legion/`, however many handoff-only pushes follow it) does not count either — so after the tester's handoff-only push onto the implementer's red, the implementer's next push does count; a push the daemon cannot classify (a listener without `changed_paths`, a list the listener stopped at 100 paths or 32,768 runes of text, a push listing no commits) does. Published once per exhausted count, not on every later red verdict for that count. Read the failed CI evidence and recovery attempts. The notice moves nothing, and no later CI red within the attempt that ran the count out (the counted head, and every head pushed on top of it until a push lands on a head CI judged green or on a settled head whose base branch requires no check and no workflow, the tester's red tests and handoff onto the held red included) sends the work back from `testing`, `reviewing`, `accepting`, `retro` or `merging` either: the issue stays in its phase until you move it, or a worker's own decision does (a tester's `fail`, a reviewer's request for changes). A head pushed onto green CI, or onto a settled head whose base branch requires no check and no workflow, leaves that attempt, so its red still comes as a `checks-red`. In `implementing`, give the implementer the failing checks (`envoy_publish` to its role topic). In any later phase a worker holds, tell that worker (`envoy_publish` to its role topic) to move the issue back to `implementing` with `request_backward_move`, naming the failing checks, and the daemon starts the implementer; or file a corrective child. In `accepting`, where no worker holds a phase, the move is yours: `request_backward_move` with `issue`, naming the failing checks. In `awaiting_merge`, where no worker holds a phase and a backward move is refused, open a `dispatch ask` naming the failing checks for the human who merges. Do not treat the blocked PR as final. A `review_round_cap` reason is the implementer's round count instead: every move back to an earlier phase counts one, and the notice comes again with each round past the cap. It moves nothing either, since the round that reached the cap has already started its worker. Tell the rounds apart by what announced each: a tester's fail by its `phase-finished` with `verdict: "fail"`; a request for changes by the reviewer's `phase-finished` over a `CHANGES_REQUESTED` review; the daemon's own move by a `checks-red` notice, whose reason names a conflict (`the head conflicts with <base>`), the required checks that failed (`CI is red at <head>`), or the merge queue's failed merge and its failing run (`the merge queue took pull request #<n> out at READY's head …`); a worker's `request_backward_move` by a `phase-finished` from the role that asked, whose reason reaches only the task of the worker the move started, so ask that worker; your own send-back from `accepting` by the reason you gave. Judge the cap by the rounds that are not conflicts. Conflict returns need nothing from you beyond the next round. Tester fails, requests for changes and CI-red returns (a `checks-red` notice is raised for CI only when a required check fails on code the branch pushed, or when the merge queue fails the merge) are the rounds the cap is for. Each `review_round_cap` notice is a decision you make in the turn it wakes, never one left for the next round to make: read what every counted round found, choose one of the three below, act on it in the same turn, and post it as a `dispatch comment` on the issue naming the rounds you read and the choice. **Narrow**: the change itself is sound but each round finds more around it; tell the implementer and the reviewer (`envoy_publish` to each one's role topic) which acceptance lines the next round answers; a correctness finding is still fixed in this pull request, and 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. On a child, file that part as a child of this issue's parent, never of this one (a parent waits in `accepting` for every child, and the split part usually builds on this pull request's unmerged code): `dispatch issue --project <project> --parent <parent> --title '<outcome>' --blocked-by <this issue> --spec-file -`. 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 accepts is a decision block in the root spec ("After approval" in `skill://legion-architect/references/design-gate.md`), and `dispatch ask` is only for a decision that needs a human's authority; neither replaces the choice. Judge a worker's backward move by the reason that worker gives. A move you ordered for a conflict found before `awaiting_merge` ("A conflicting head" in `skill://legion-architect/references/acceptance.md`) is a conflict return. The merger's move back to `reviewing` for a READY refusal (`READY_UNREVIEWED_CHANGE`, or `READY_NOT_APPROVED` when its task names no approved head), and the reviewer's no-fix request for changes that follows it so the tester runs the head, are not the work going in circles either. A move that sends back code found wrong counts with the tester fails. |
|
|
30
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
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
32
|
| `merge-refused` | GitHub refused the daemon's submission of READY's merge in a way a retry would meet again, so it submits that merge no further: ask a person with `dispatch ask` to fix the repository setting or App permission and merge at READY's head (`skill://legion-architect/references/merge-notices.md#merge-refused`). |
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: legion-controller
|
|
3
|
-
description: Use when you are a Legion project's controller session, on any controller wake (triage, slot-free, todo, tick, unslotted, held, worker-died), weekly-plan ranking, backlog stewardship, a finding ask, an issue a person asks you to file, the daily report, an architect's escalation, or a person's message or mention.
|
|
3
|
+
description: Use when you are a Legion project's controller session, on any controller wake (triage, slot-free, todo, tick, unslotted, held, worker-died), weekly-plan ranking, backlog stewardship, a clear defect or finding ask a role sends, an issue a person asks you to file, the daily report, an architect's escalation, or a person's message or mention.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Legion Controller
|
|
7
7
|
|
|
8
8
|
If this skill disagrees with the code, a command's output or its `--help`, follow those and say which line of this skill is wrong.
|
|
9
9
|
|
|
10
|
-
You are the one persistent, wake-driven session of a Legion project. You start no work yourself
|
|
10
|
+
You are the one persistent, wake-driven session of a Legion project. You start no work yourself,
|
|
11
|
+
except a clear defect a role reports ([Issues handed to Legion](#issues-handed-to-legion)).
|
|
11
12
|
|
|
12
13
|
## Rules
|
|
13
14
|
|
|
@@ -21,7 +22,7 @@ You are the one persistent, wake-driven session of a Legion project. You start n
|
|
|
21
22
|
- Never edit a repository, push a commit or call `handoff_complete`, and never forward a raw event to an architect, because the daemon runs the phases and raw events only fill the architect's context.
|
|
22
23
|
- Never open a design gate, because only a person's approval of the root's spec in Dispatch opens it.
|
|
23
24
|
- When a `dispatch` command fails, or a `curl` read fails or prints no JSON array, act on nothing it would decide and name its first line in your summary (the turn's last message), because an empty read is no evidence.
|
|
24
|
-
- Write every Dispatch comment, message and ask as `skill://dispatch`'s "
|
|
25
|
+
- Write every Dispatch comment, message and ask as `skill://dispatch-writing-specs`'s "Rules for the text" say.
|
|
25
26
|
|
|
26
27
|
Launch, `gh`, pull requests, takeover: `skill://legion-controller/references/session.md`.
|
|
27
28
|
|
|
@@ -62,6 +63,7 @@ Wake details: `skill://legion-controller/references/wakes.md`.
|
|
|
62
63
|
| `worker-died on <KEY>` (`role: "architect"`) | Re-admit once its cause is gone, else park it |
|
|
63
64
|
| An architect's escalation | `skill://legion-controller/references/escalation.md` |
|
|
64
65
|
| A finding ask's reference, or its answer | `skill://legion-controller/references/finding-asks.md` |
|
|
66
|
+
| A clear defect a role sends | `skill://legion-controller/references/finding-asks.md#hand-over-a-clear-defect` |
|
|
65
67
|
| A finding sent as text | Open an ask ([Findings](#findings-and-the-issues-you-file)) |
|
|
66
68
|
| READY, a scope question, a routing request | `skill://legion-controller/references/backlog-stewardship.md` |
|
|
67
69
|
| A GitHub mention | Answer it, or route it to the owning architect |
|
|
@@ -77,13 +79,18 @@ claim is free; claim it again.` to `notifications.role.<issues.<KEY>.architect.l
|
|
|
77
79
|
## Issues handed to Legion
|
|
78
80
|
|
|
79
81
|
The daemon works only issues carrying the Dispatch label `legion` (any case). Work reaches Legion
|
|
80
|
-
|
|
82
|
+
by a person's decision: they set the label from the issue header, or ask you to (a direct
|
|
81
83
|
message, a Slack request that asks Legion to do the work, their answer to a finding ask, or a
|
|
82
|
-
ruling the weekly plan records with its link).
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
for
|
|
84
|
+
ruling the weekly plan records with its link). The one exception is a clear defect a role hands
|
|
85
|
+
you whose code no running tree owns: you file it with the label, because its fix is how to do work
|
|
86
|
+
a person already wants, and humans decide what, agents decide how (the operator's answer on one
|
|
87
|
+
such case: a clear defect is fixed, not asked about). A defect whose code a running
|
|
88
|
+
tree owns is that tree's, and you file nothing for it. Label an issue only then, because
|
|
89
|
+
a labelled root in `todo` is admitted, so an unasked label starts unasked work. Never take a
|
|
90
|
+
person's label off, because it is their decision. Label an issue before you `set_status` it to
|
|
91
|
+
`todo`, because `todo` admits only a labelled root. How:
|
|
92
|
+
`skill://legion-controller/references/triage.md` "Label an issue for a person". Admission states:
|
|
93
|
+
`skill://legion-controller/references/admission.md`.
|
|
87
94
|
|
|
88
95
|
## Free slots
|
|
89
96
|
|
|
@@ -95,7 +102,7 @@ it, and cite that request. The free slots are `admission.cap` minus the roots in
|
|
|
95
102
|
a blocked root takes no slot until its blockers are done. Every move to `todo` you make is one of
|
|
96
103
|
these:
|
|
97
104
|
|
|
98
|
-
- a root a person handed over, at triage;
|
|
105
|
+
- a root a person handed over, or a root you filed for a clear defect no tree owns, at triage;
|
|
99
106
|
- an issue a person asks you to start;
|
|
100
107
|
- a tree you re-admit after its architect failed or after a hold, once you checked its cause is
|
|
101
108
|
gone, or because a person asks;
|
|
@@ -104,11 +111,14 @@ these:
|
|
|
104
111
|
## Findings, and the issues you file
|
|
105
112
|
|
|
106
113
|
File an issue only when a person asks you to (a direct message, a Slack request, or their answer
|
|
107
|
-
to a finding ask),
|
|
108
|
-
a role
|
|
109
|
-
`skill://legion-
|
|
110
|
-
|
|
111
|
-
`
|
|
114
|
+
to a finding ask), for a clear defect no running tree owns, and the report issue. A clear defect,
|
|
115
|
+
yours or a role's, goes first to the tree that owns its code, and becomes a Legion root only when
|
|
116
|
+
no tree does: `skill://legion-controller/references/finding-asks.md#hand-over-a-clear-defect`.
|
|
117
|
+
Every other problem is a finding: one you find, or one a role sends you as text. Open a
|
|
118
|
+
`dispatch ask` for it on the report issue, as `skill://legion-worker/references/findings.md` says,
|
|
119
|
+
and file nothing until a person answers, because a person decides whether it needs an issue and
|
|
120
|
+
where. Each issue you file carries a `Filed for` line:
|
|
121
|
+
`skill://legion-controller/references/finding-asks.md`.
|
|
112
122
|
|
|
113
123
|
## Daily report
|
|
114
124
|
|
|
@@ -119,4 +129,5 @@ and why, and what runs: `skill://legion-controller/references/daily-report.md`.
|
|
|
119
129
|
|
|
120
130
|
The operator's deployment instructions in your system prompt (required checks, deploy commands,
|
|
121
131
|
code owners, roles you may consult) override this skill where the two conflict, and may narrow
|
|
122
|
-
what you label for Legion. They never hand Legion work,
|
|
132
|
+
what you label for Legion. They never hand Legion work: a person does, or a role reports a clear
|
|
133
|
+
defect.
|
|
@@ -53,11 +53,26 @@ For a READY packet, confirm the Dispatch record and leave the merge to the daemo
|
|
|
53
53
|
use a GitHub credential to merge from the controller, because the daemon submits the merge itself
|
|
54
54
|
at READY's head.
|
|
55
55
|
|
|
56
|
+
## The owner audit
|
|
57
|
+
|
|
58
|
+
At each pass, read `dispatch issues --project <KEY> --priority 0 --priority 1 --all`:
|
|
59
|
+
|
|
60
|
+
- An unclaimed row in `triage`, `icebox`, `backlog` or `todo` is a decision: someone builds it, or
|
|
61
|
+
it closes. A claimed or in-flight row already has an owner.
|
|
62
|
+
- A close that says a defect cannot happen cites the code that prevents it; without that, the issue
|
|
63
|
+
is unread, not foreclosed.
|
|
64
|
+
- A plan or measurement nobody builds needs an owner.
|
|
65
|
+
- A merged pull request names an issue not yet in `testing` or `done`: check it live, then move it.
|
|
66
|
+
- Someone repeats the issue's work by hand: fix its status.
|
|
67
|
+
- A route nobody holds (`--route-status no_holder`, any priority), after the two reads that
|
|
68
|
+
"Recovering stopped-session work" names: staff the role, route the issue to a live holder, or
|
|
69
|
+
clear the route and assign it.
|
|
70
|
+
|
|
56
71
|
## Recovering stopped-session work
|
|
57
72
|
|
|
58
73
|
Treat a session route as stopped only after two reads of
|
|
59
|
-
`dispatch issues --project <KEY> --route-status no_holder --
|
|
60
|
-
both list it, as
|
|
74
|
+
`dispatch issues --project <KEY> --route-status no_holder --all` at least ten minutes apart
|
|
75
|
+
both list it, as `dispatch issues --help` says for `route_status`, because a
|
|
61
76
|
restarting session is absent for minutes. Do not poll to create the second read; use the next
|
|
62
77
|
controller wake. Then confirm with `envoy_sessions` that no live session owns the work (the
|
|
63
78
|
session the route names, or one that took it over), because a live owner still holds it.
|
|
@@ -17,7 +17,7 @@ reach that person's Inbox.
|
|
|
17
17
|
dispatch issue --project <PROJECT> --title 'Legion daily report' --assignee '<email>' --spec-file - <<'EOF'
|
|
18
18
|
## Summary
|
|
19
19
|
|
|
20
|
-
Legion's controller posts one message here each day: what Legion finished, what it closed without a change and why, and what is running. Legion's agents also
|
|
20
|
+
Legion's controller posts one message here each day: what Legion finished, what it closed without a change and why, and what is running. Legion's agents also post here the evidence of each clear defect they found outside their own work, which the controller hands to the tree that owns the code, or files for Legion when no tree does. They ask here whether a problem that needs a decision needs an issue. This issue is not work, so it carries no `legion` label and stays in icebox.
|
|
21
21
|
EOF
|
|
22
22
|
legion({ op: "set_status", issue: "<report KEY>", status: "icebox" })
|
|
23
23
|
```
|
|
@@ -49,11 +49,11 @@ post when its `Events:` show no `message.created` from today.
|
|
|
49
49
|
## What
|
|
50
50
|
|
|
51
51
|
One `dispatch message --issue <report KEY> --body-file -` of at most 2,000 characters, because a
|
|
52
|
-
person reads every report in their Inbox. Write it as `skill://dispatch`'s "
|
|
53
|
-
every issue by its
|
|
52
|
+
person reads every report in their Inbox. Write it as `skill://dispatch-writing-specs`'s "Rules for the text" say:
|
|
53
|
+
every issue by its `dispatch://` link and what it does for the reader, every pull request by its URL.
|
|
54
54
|
|
|
55
55
|
- **Finished.** `dispatch issues --project <PROJECT> --status done --updated-since <the previous
|
|
56
|
-
report's time, or 24 hours ago> --
|
|
56
|
+
report's time, or 24 hours ago> --all`, keeping the issues `read_state` records
|
|
57
57
|
under `issues` whose `issue.closed` event is after the previous report. `dispatch read` each: the
|
|
58
58
|
pull request under `External links:` is the one that merged.
|
|
59
59
|
- **Closed without a change.** Those with no pull request, each with the reason its closing
|
|
@@ -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
|