@sjawhar/opencode-legion-envoy 9.1.0 → 9.3.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 +211 -35
- package/dist/src/server.js +54 -13
- package/package.json +1 -1
- package/skills/dispatch/SKILL.md +12 -12
- package/skills/envoy/SKILL.md +1 -1
- package/skills/envoy/references/deliveries.md +1 -1
- package/skills/legion-architect/SKILL.md +10 -7
- package/skills/legion-architect/references/acceptance.md +7 -2
- package/skills/legion-architect/references/decomposition.md +3 -2
- package/skills/legion-architect/references/design-gate.md +9 -1
- package/skills/legion-architect/references/wake-routing.md +5 -5
- package/skills/legion-controller/SKILL.md +5 -2
- package/skills/legion-controller/references/admission.md +10 -8
- package/skills/legion-controller/references/wakes.md +22 -8
- package/skills/legion-worker/SKILL.md +2 -2
- package/skills/legion-worker/references/merge-gate.md +12 -11
- package/skills/legion-worker/references/skip-checks-trailer.md +1 -1
- package/skills/legion-worker/references/tools.md +3 -2
|
@@ -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.
|
|
@@ -46,7 +46,7 @@ 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
|
|
@@ -23,10 +23,10 @@ it, and any other change to the head voids it.
|
|
|
23
23
|
`task(agent="thermonuclear-code-quality")` once at that head — the head the round reviews —
|
|
24
24
|
and records the verdict. The CI it reads is the checks that stand for the code under review:
|
|
25
25
|
those GitHub ran for the newest push of the branch that started `pull_request` workflows, when
|
|
26
|
-
every commit above that push's head changes only `.legion
|
|
27
|
-
head whose commit message carries `skip-checks: true`, which every handoff-only push
|
|
28
|
-
retro does; retro's push carries no such line, so its head does get GitHub's workflow runs
|
|
29
|
-
its own). With `<o>/<r>`, `<n>`, `<base>`, `<headRefName>` and `<headRefOid>` from
|
|
26
|
+
every commit above that push's head changes only `.legion/<issue>/` (GitHub starts no workflow
|
|
27
|
+
run for a head whose commit message carries `skip-checks: true`, which every handoff-only push
|
|
28
|
+
before retro does; retro's push carries no such line, so its head does get GitHub's workflow runs
|
|
29
|
+
of its own). With `<o>/<r>`, `<n>`, `<base>`, `<headRefName>` and `<headRefOid>` from
|
|
30
30
|
`gh pr view <n> --json baseRefName,headRefOid,headRefName`, four reads. The required set is the
|
|
31
31
|
base branch's rulesets' required checks and its branch protection's, when it has any. Both
|
|
32
32
|
branch endpoints take the base as a URL path, each segment escaped and each `/` kept (a raw `#`
|
|
@@ -46,8 +46,9 @@ it, and any other change to the head voids it.
|
|
|
46
46
|
newest first). That commit's check-runs
|
|
47
47
|
(`gh api repos/<o>/<r>/commits/<ci head>/check-runs --paginate --jq '.check_runs[] | [.name, .status, .conclusion] | @tsv'`):
|
|
48
48
|
every required name present, `completed` and `success` (GitHub counts `neutral` and `skipped`
|
|
49
|
-
as passing for a required check). And the compare proving only `.legion
|
|
50
|
-
|
|
49
|
+
as passing for a required check). And the compare proving only `.legion/<issue>/` changed since,
|
|
50
|
+
`<issue>` being your `LEGION_ISSUE` (another issue's `.legion/` is code to the daemon and READY)
|
|
51
|
+
(`gh api repos/<o>/<r>/compare/<ci head>...<headRefOid> --jq '{handoffsOnly: ([.files[] | .filename, (.previous_filename // empty)] | all(startswith(".legion/<issue>/"))), files: (.files | length)}'`
|
|
51
52
|
prints `handoffsOnly` `true`; `false` means code was pushed since CI ran, so wait for the new
|
|
52
53
|
push's runs and read the CI head again). GitHub's compare lists at most 300 files for the whole
|
|
53
54
|
comparison, so `files` at 300 is a comparison GitHub cut at its cap and `handoffsOnly` decides
|
|
@@ -98,7 +99,7 @@ it, and any other change to the head voids it.
|
|
|
98
99
|
`REQUEST_CHANGES` when the code is at fault, and your own commit to fix when your prose broke
|
|
99
100
|
it. The architect reads your prose commits at its acceptance (*The architect's acceptance*,
|
|
100
101
|
below), and that read is no new round. A round after a READY refusal is the one exception. Its
|
|
101
|
-
approval needs the tester's `E2E` line to name the head with only `.legion
|
|
102
|
+
approval needs the tester's `E2E` line to name the head with only `.legion/<issue>/` changed since. So
|
|
102
103
|
there you fix no prose yourself, and you name each prose finding in a `REQUEST_CHANGES`.
|
|
103
104
|
|
|
104
105
|
A reviewer's phase ends with its completion, not with its review. Every round writes a handoff
|
|
@@ -109,7 +110,7 @@ whatever CI says and the issue leaves reviewing with it. The approval waits on n
|
|
|
109
110
|
handoff-only commit either: it ends the round whatever CI says there, since the round reads no CI
|
|
110
111
|
verdict, and a red the daemon reads later — at that commit or any above it — is the code's red
|
|
111
112
|
and resumes the implementer from wherever the issue is (reviewing, retro or merging) without the
|
|
112
|
-
reviewer. A head your own push changed beyond `.legion
|
|
113
|
+
reviewer. A head your own push changed beyond `.legion/<issue>/` is the exception. The daemon marks it a
|
|
113
114
|
planned red and sends nothing back for it, so wait for its checks yourself (*Fix a prose-only
|
|
114
115
|
finding yourself*, above). A review of a head the handoff push then replaces names a head the pull
|
|
115
116
|
request no longer has. The daemon moves the issue once both are in — the decision GitHub reports
|
|
@@ -120,8 +121,8 @@ reviewing until you finish.
|
|
|
120
121
|
|
|
121
122
|
- Between the reviewer's approval and retro the issue sits in `accepting` (Dispatch status
|
|
122
123
|
`needs_review`), where no worker is started: the tree's architect reads the evidence (the
|
|
123
|
-
tester's handoff, the PR body's `E2E (implementer)` and `E2E (tester)` lines
|
|
124
|
-
approval of the head by SHA
|
|
124
|
+
tester's handoff, the PR body's `E2E (implementer)` and `E2E (tester)` lines and the reviewer's
|
|
125
|
+
approval of the head by SHA; children still running do not hold it) and accepts with the
|
|
125
126
|
`legion` tool's `sign_off`, which moves the issue to retro, or sends it back to `implementing`
|
|
126
127
|
or `testing` with `request_backward_move` and a reason, which reaches the started worker's task
|
|
127
128
|
as its `Reason:` line and counts a review round. The architect also reads the reviewer's own
|
|
@@ -130,7 +131,7 @@ reviewing until you finish.
|
|
|
130
131
|
controller takes it"). The acceptance asks no `Production:` line of anyone: that line is written
|
|
131
132
|
after the close (*The production check after the close*, below).
|
|
132
133
|
CI red at the head in `accepting` returns the issue to `implementing` as it does in review. A
|
|
133
|
-
head the review App pushed beyond `.legion
|
|
134
|
+
head the review App pushed beyond `.legion/<issue>/` is the exception: the daemon marks it a planned red
|
|
134
135
|
and acts on it nowhere, and GitHub holds the merge under the base branch's own rules.
|
|
135
136
|
|
|
136
137
|
## Retro
|
|
@@ -8,7 +8,7 @@ sjawhar/legion.
|
|
|
8
8
|
|
|
9
9
|
## The shape GitHub honours
|
|
10
10
|
|
|
11
|
-
A push whose commits touch only `.legion
|
|
11
|
+
A push whose commits touch only `.legion/<issue>/` (a handoff-only push) ends its head's commit message
|
|
12
12
|
with `skip-checks: true` as the last line, preceded by two empty lines, so GitHub starts no
|
|
13
13
|
workflow run for it and the daemon carries the code head's verdict to it — except retro's push,
|
|
14
14
|
which never carries the line whatever paths it touches, for the reason the next section gives.
|
|
@@ -14,8 +14,9 @@ which you have; those need no runner.
|
|
|
14
14
|
- `psql` — the Dispatch browser suite (`bun run e2e`) resets its database for every spec through
|
|
15
15
|
`packages/dispatch/e2e/psql.ts`, which `seed.ts` calls, and the local stack (`bun run
|
|
16
16
|
dispatch:local`, `packages/dispatch/e2e/local-instance.ts`) refuses to start without `go`,
|
|
17
|
-
`psql`, `flock` and `
|
|
18
|
-
given. The image carries `go`, `flock` and `
|
|
17
|
+
`psql`, `flock`, `openssl` and `python3` on `PATH`, and without `docker` too unless a
|
|
18
|
+
`DATABASE_URL` is given. The image carries `go`, `flock`, `openssl` and `python3`, a
|
|
19
|
+
`DATABASE_URL` stands in for `docker`,
|
|
19
20
|
and `psql` was the one a role had no way to supply — so a role whose change owes that suite can
|
|
20
21
|
run it.
|
|
21
22
|
- `redis-cli` — no repository of the project speaks Redis today; it is carried anyway, at about a
|