@sjawhar/pi-legion 10.23.1 → 10.25.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/legion.js +1 -1
- package/dist/skills/legion-architect/SKILL.md +1 -1
- package/dist/skills/legion-architect/references/merge-notices.md +63 -0
- package/dist/skills/legion-architect/references/wake-routing.md +8 -3
- package/dist/skills/legion-retro/SKILL.md +3 -3
- package/dist/skills/legion-worker/SKILL.md +16 -29
- package/dist/skills/legion-worker/references/conflicts-and-rewrites.md +1 -1
- package/dist/skills/legion-worker/references/merge-gate.md +19 -15
- package/dist/skills/legion-worker/references/pr-body.md +6 -15
- package/dist/skills/legion-worker/references/review-threads.md +4 -1
- package/dist/skills/legion-worker/references/skip-checks-trailer.md +47 -0
- package/package.json +1 -1
package/dist/legion.js
CHANGED
|
@@ -16937,7 +16937,7 @@ async function bootLegionClaim(host2) {
|
|
|
16937
16937
|
// package.json
|
|
16938
16938
|
var package_default = {
|
|
16939
16939
|
name: "@sjawhar/pi-legion",
|
|
16940
|
-
version: "10.
|
|
16940
|
+
version: "10.25.0",
|
|
16941
16941
|
license: "Apache-2.0",
|
|
16942
16942
|
type: "module",
|
|
16943
16943
|
omp: {
|
|
@@ -314,7 +314,7 @@ The reviewer's approval of the head moves the issue to `accepting` (its Dispatch
|
|
|
314
314
|
notice with an `APPROVED` review is the cue. Read the evidence against every acceptance line of the
|
|
315
315
|
spec and the plan: the tester's handoff (`.legion/<issue>/test.json` at the approved head), the PR
|
|
316
316
|
body's `E2E (implementer)` and `E2E (tester)` lines, the reviewer's approval of that head by SHA
|
|
317
|
-
(`gh api repos/<o>/<r>/pulls/<n>/reviews`). Accept only when scope is fully met, integration
|
|
317
|
+
(`gh api --paginate repos/<o>/<r>/pulls/<n>/reviews`). Accept only when scope is fully met, integration
|
|
318
318
|
evidence is current, corrective work is complete, review is clean, and no necessary work was
|
|
319
319
|
silently deferred. Post the acceptance as a
|
|
320
320
|
`dispatch comment` explicit about that evidence, then accept with `sign_off`, which runs only in
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# The merge notices: what each one means and what you do
|
|
2
|
+
|
|
3
|
+
Part of `skill://legion-architect`. Read the section for the notice that woke you. Each of
|
|
4
|
+
`merge-refused`, `merge-stalled`, `merge-removed` and `merge-waiting` is the daemon reporting on
|
|
5
|
+
the merge it submitted for an accepted READY, and each reaches the architect that owns the issue.
|
|
6
|
+
The wake-routing table, in `skill://legion-architect/references/wake-routing.md`, gives each
|
|
7
|
+
notice its move in one sentence; the section here is what that sentence stands on — what the
|
|
8
|
+
notice's `reason` names, what the daemon has and has not stopped doing, and what you read before
|
|
9
|
+
you ask a person.
|
|
10
|
+
|
|
11
|
+
In every one of the four the issue stays in `awaiting_merge`, where no worker holds a phase and a
|
|
12
|
+
backward move is refused, so what a repository rule needs is a person's to give.
|
|
13
|
+
|
|
14
|
+
## `merge-refused`
|
|
15
|
+
|
|
16
|
+
Its `reason` names the pull request, READY's head, what the daemon asked GitHub to do (merge,
|
|
17
|
+
enqueue, or enable auto-merge for it), and GitHub's refusal, which a retry would meet again
|
|
18
|
+
(squash merges disabled, a permission the implement App lacks). The daemon has stopped submitting
|
|
19
|
+
that merge; the issue stays in `awaiting_merge`. The cause is a repository setting or an App
|
|
20
|
+
permission, a person's to change: ask one with `dispatch ask`, naming the refusal and the pull
|
|
21
|
+
request at READY's head, which that person merges under the repository's rules once it is fixed.
|
|
22
|
+
The merge closes the issue. Never bypass a rule.
|
|
23
|
+
|
|
24
|
+
## `merge-stalled`
|
|
25
|
+
|
|
26
|
+
Its `reason` names the pull request, the state and head GitHub shows (closed, or another head),
|
|
27
|
+
since when GitHub has shown it, and READY's head, which the record still awaits. GitHub's last
|
|
28
|
+
change to the pull request is more than 10 minutes old and the record never heard of it, so its
|
|
29
|
+
webhook never reached the daemon: the daemon has stopped submitting that merge, and the issue
|
|
30
|
+
stays in `awaiting_merge`. Read the pull request on GitHub. Open at READY's head, GitHub has
|
|
31
|
+
caught up: a person merges it under the repository's rules, and the merge closes the issue.
|
|
32
|
+
Closed, or at another head the daemon never recorded: ask a person with `dispatch ask`, naming the
|
|
33
|
+
pull request, what GitHub shows, and READY's head. Never bypass a rule.
|
|
34
|
+
|
|
35
|
+
## `merge-removed`
|
|
36
|
+
|
|
37
|
+
Its `reason` names who took the pull request out of the merge queue or turned its auto-merge off
|
|
38
|
+
at READY's head after the daemon submitted it, or says GitHub turned it off on its own (over a
|
|
39
|
+
repository setting such as a merge method the repository blocks) and names the App that submitted
|
|
40
|
+
the merge, which GitHub names as the actor of its own disables; then what was removed, GitHub's
|
|
41
|
+
reason (`manual`, `merge_conflict`, `manually_disabled`, `merge_commit_blocked`, …), when, and
|
|
42
|
+
READY's head. It was not a failed merge, which comes as a `checks-red` instead. The daemon submits
|
|
43
|
+
that merge no further, so the removal stands, and the issue stays in `awaiting_merge`. Read the
|
|
44
|
+
pull request on GitHub. A `merge_conflict` conflicts with the base or with a pull request queued
|
|
45
|
+
ahead of it: once the conflict is on the base, the daemon's own conflict check sends the issue
|
|
46
|
+
back to `implementing` by itself. Otherwise ask a person with `dispatch ask` (the one who removed
|
|
47
|
+
it, when the reason names a person rather than GitHub on its own), naming the pull request at
|
|
48
|
+
READY's head and the reason; that person merges or queues it again under the repository's rules,
|
|
49
|
+
and the merge closes the issue. Never bypass a rule.
|
|
50
|
+
|
|
51
|
+
## `merge-waiting`
|
|
52
|
+
|
|
53
|
+
Its `reason` names the pull request, READY's head, how long the row has waited and what GitHub
|
|
54
|
+
last answered. GitHub has not completed the merge the daemon submitted within an hour of the
|
|
55
|
+
workflow queueing it at READY's acceptance. Nothing has been withdrawn: the merge stands, the
|
|
56
|
+
issue stays in `awaiting_merge`, and the daemon goes on submitting it, so there is nothing to undo
|
|
57
|
+
and you are told once, however much longer it waits. Read the pull request on GitHub and find what
|
|
58
|
+
GitHub is waiting for: a required check that never ran, a required check that failed, a review
|
|
59
|
+
only a person can give, or a merge queue that has not landed it. Act on what you find. In
|
|
60
|
+
`awaiting_merge` no worker holds a phase and a backward move is refused, so what a check, a review
|
|
61
|
+
or a repository rule needs is a person's: ask one with `dispatch ask`, naming the pull request at
|
|
62
|
+
READY's head and what GitHub waits on; the merge closes the issue when GitHub completes it,
|
|
63
|
+
whoever unblocks it. Never bypass a rule.
|
|
@@ -7,6 +7,10 @@ root. A wake about an architect role's own launch reaches the architect above it
|
|
|
7
7
|
worker lifecycle, child lifecycle, and design-gate wakes are architect-only and never go to an
|
|
8
8
|
active phase worker.
|
|
9
9
|
|
|
10
|
+
The four merge-notice rows are one sentence each; read
|
|
11
|
+
`skill://legion-architect/references/merge-notices.md` for the notice that woke you — what its
|
|
12
|
+
`reason` names, what the daemon has stopped doing, and what you read before you ask a person.
|
|
13
|
+
|
|
10
14
|
| Wake | Procedure |
|
|
11
15
|
| --- | --- |
|
|
12
16
|
| `carried-gate-opened` | Only a root whose catch-up said `carried` gets it. Its `version` and `reason`: a human approved the old root's spec document at `version`, which opened the gate this root carried; the daemon has released any READY it refused and started any child waiting on it. It is not `design-approved`: do not proceed to section 2 or release a wave because of it, and start nothing. |
|
|
@@ -20,9 +24,10 @@ active phase worker.
|
|
|
20
24
|
| `pr-blocked` | Its `reason` names the pull request and the `max_fix_attempts` it reached, or reads `Issue reached review_round_cap=<N>.` (the end of this row). The count is of heads pushed onto a red verdict that changed something outside `.legion/` — handoff-only pushes (`.legion/` paths only) never count, a push by the review App (a planner's, tester's, reviewer's or architect's) never counts, and the head after a red the tester's red tests earned (a review-App push that changed a path outside `.legion/`, however many handoff-only pushes follow it) does not count either — so after the tester's handoff-only push onto the implementer's red, the implementer's next push does count; a push the daemon cannot classify (a listener without `changed_paths`, a list the listener stopped at 100 paths or 32,768 runes of text, a push listing no commits) does. Published once per exhausted count, not on every later red verdict for that count. Read the failed CI evidence and recovery attempts. The notice moves nothing, and no later CI red within the attempt that ran the count out (the counted head, and every head pushed on top of it until a push lands on a head CI judged green, the tester's red tests and handoff onto the held red included) sends the work back from `testing`, `reviewing`, `accepting`, `retro` or `merging` either: the issue stays in its phase until you move it, or a worker's own decision does (a tester's `fail`, a reviewer's request for changes). A head pushed onto green CI leaves that attempt, so its red still comes as a `checks-red`. In `implementing`, give the implementer the failing checks (`envoy_publish` to its role topic). In any later phase a worker holds, tell that worker (`envoy_publish` to its role topic) to move the issue back to `implementing` with `request_backward_move`, naming the failing checks, and the daemon starts the implementer; or file a corrective child. In `accepting`, where no worker holds a phase, the move is yours: `request_backward_move` with `issue`, naming the failing checks. In `awaiting_merge`, where no worker holds a phase and a backward move is refused, open a `dispatch ask` naming the failing checks for the human who merges, as section 6 does for a conflict there. Do not treat the blocked PR as final. A `review_round_cap` reason is the implementer's round count instead: every move back to an earlier phase counts one, and the notice comes again with each round past the cap. It moves nothing either, since the round that reached the cap has already started its worker. Tell the rounds apart by what announced each: a tester's fail by its `phase-finished` with `verdict: "fail"`; a request for changes by the reviewer's `phase-finished` over a `CHANGES_REQUESTED` review; the daemon's own move by a `checks-red` notice, whose reason names a conflict (`the head conflicts with <base>`), the required checks that failed (`CI is red at <head>`), or the merge queue's failed merge and its failing run (`the merge queue took pull request #<n> out at READY's head …`); a worker's `request_backward_move` by a `phase-finished` from the role that asked, whose reason reaches only the task of the worker the move started, so ask that worker; your own send-back from `accepting` by the reason you gave. Judge the cap by the rounds that are not conflicts. Conflict returns need nothing from you beyond the next round. Tester fails, requests for changes and CI-red returns (a `checks-red` notice is raised for CI only when a required check fails on code the branch pushed, or when the merge queue fails the merge) are the rounds the cap is for. Each `review_round_cap` notice is a decision you make in the turn it wakes, never one left for the next round to make: read what every counted round found, choose one of the three below, act on it in the same turn, and post it as a `dispatch comment` on the issue naming the rounds you read and the choice. **Narrow**: the change itself is sound but each round finds more around it; tell the implementer and the reviewer (`envoy_publish` to each one's role topic) which acceptance lines the next round answers; a correctness finding is still fixed in this pull request, and only naming, duplication or wording cleanup moves to the `Fast-follow:` line. **Split**: part of the work cannot settle in this pull request. On a child, file that part as a child of this issue's parent, never of this one (a parent waits in `accepting` for every child, and the split part usually builds on this pull request's unmerged code): `dispatch issue --project <project> --parent <parent> --title '<outcome>' --blocked-by <this issue> --spec-file -`, released with `release_children` once this issue closes. On the root, narrow it to what settles here and open a `dispatch ask` for the rest (`skill://legion-worker/references/findings.md`): a person decides whether that becomes an issue. Tell the implementer and the reviewer that this pull request no longer carries it. **Accept the rounds**: each round found less than the one before it, and the last found no defect in the change itself (a stale line, a comment that states an old default); let the loop run and say why in the comment. A later notice is a new decision: accept again only when its round found less than the round you last accepted, and otherwise narrow or split. A finding that changes what the spec accepts is a decision block in the root spec (section 1), and `dispatch ask` is only for a decision that needs a human's authority; neither replaces the choice. Judge a worker's backward move by the reason that worker gives. A move you ordered for a conflict found before `awaiting_merge` (section 6) is a conflict return. The merger's move back to `reviewing` for a READY refusal (`READY_UNREVIEWED_CHANGE`, or `READY_NOT_APPROVED` when its task names no approved head), and the reviewer's no-fix request for changes that follows it so the tester runs the head, are not the work going in circles either. A move that sends back code found wrong counts with the tester fails. |
|
|
21
25
|
| `pr-merged` | The pull request merged and the issue did not close; its `reason` says why. Merged before the issue reached `awaiting_merge`: the workflow runs on, and the merge closes the issue the moment it gets there, your acceptance and retro first. Merged while the rest of the tree still runs: the reason names the pull request and every issue the close waits for, and the root waits in `awaiting_merge`, slot and workers kept, until the last of them is done or parked (section 7). Either way you start nothing and no further notice follows. |
|
|
22
26
|
| `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. |
|
|
23
|
-
| `merge-refused` |
|
|
24
|
-
| `merge-stalled` |
|
|
25
|
-
| `merge-removed` |
|
|
27
|
+
| `merge-refused` | GitHub refused the daemon's submission of READY's merge in a way a retry would meet again, so it submits that merge no further: ask a person with `dispatch ask` to fix the repository setting or App permission and merge at READY's head (`skill://legion-architect/references/merge-notices.md#merge-refused`). |
|
|
28
|
+
| `merge-stalled` | GitHub shows the pull request closed, or at another head, while the record still awaits READY's head, and that change's webhook never reached the daemon: read the pull request on GitHub and act on what it shows (`skill://legion-architect/references/merge-notices.md#merge-stalled`). |
|
|
29
|
+
| `merge-removed` | A person, or GitHub on its own, took the pull request out of the merge queue or turned its auto-merge off at READY's head after the daemon submitted the merge: read the pull request on GitHub and act on the removal's reason (`skill://legion-architect/references/merge-notices.md#merge-removed`). |
|
|
30
|
+
| `merge-waiting` | GitHub has not completed the merge the daemon submitted within an hour of READY's acceptance, and nothing has been withdrawn: read the pull request on GitHub for what GitHub waits on and ask a person for what no worker can do (`skill://legion-architect/references/merge-notices.md#merge-waiting`). |
|
|
26
31
|
| A reply on an ask you follow | Interpret the reply in the issue's design context. Answer in its thread (`dispatch comment --reply-to <comment id>`; under an open ask whose next move is yours, such as the approval request you must revise or hand back, `--reply-to-ask <ask id> --turn agent`, since a default-turn reply hands that request back to the human and a corrected `--summary` is then refused), then adjust the plan or relay it via `envoy_publish` to the responsible worker's role token; scope and product decisions remain with you. |
|
|
27
32
|
| `worker-died` | Its `role` and `phase`. That role's claim failed: its launches or prompts ran out. For a phase worker the daemon holds the issue (phase `held`) and starts nothing more on it. Reassess the work, then decide with `retry_or_escalate`: `retry` when the failure looks agent-specific or transient, `escalate` to hand the held issue to the controller when it looks environmental. |
|
|
28
33
|
| `held` | Its `role` and `phase` (the phase the issue left), or `phase` with `reason: "escalated"`. Without `reason`, that phase's worker ran out of launches or prompts: the issue is held (phase `held`), the `worker-died` that comes with it is yours to answer with `retry_or_escalate` as that row says, and the controller hears of the hold too. With `reason: "escalated"`, it records your own `escalate`: the controller has the issue now, and you start nothing for it. |
|
|
@@ -133,9 +133,9 @@ Before you push those commits, bring up to date the body content the repository
|
|
|
133
133
|
pull request's changed paths. A repository can require such content, a line naming a checklist
|
|
134
134
|
for each class of changed path for instance, and have a required check read it from the PR body.
|
|
135
135
|
When your commits add a `docs/solutions/` path the approved body never accounted for, or take away
|
|
136
|
-
the `.legion/` paths it did, that check fails at your head; the merger reports a stale body
|
|
137
|
-
than rewriting it, and
|
|
138
|
-
|
|
136
|
+
the `.legion/` paths it did, that check fails at your head; the merger reports a stale body
|
|
137
|
+
rather than rewriting it, and GitHub will not merge a head whose required check failed, so the
|
|
138
|
+
merge waits.
|
|
139
139
|
|
|
140
140
|
1. Read what the deployment instructions in your system prompt, and the agent guide and pull
|
|
141
141
|
request template of the repository you are working in, derive from the changed paths. When
|
|
@@ -25,6 +25,7 @@ with the `read` tool: a read by filesystem path stops at 300 lines.
|
|
|
25
25
|
| Before a push, to check your commits' App identity against your issue's default branch | `skill://legion-worker/references/default-branch-check.md` |
|
|
26
26
|
| You reply on or resolve a review thread, list a pull request's threads, or adjudicate a bot's finding | `skill://legion-worker/references/review-threads.md` |
|
|
27
27
|
| GitHub reports a conflict, the pull request is retargeted, you compare heads after a conflict merge, or you would rewrite a pushed commit | `skill://legion-worker/references/conflicts-and-rewrites.md` |
|
|
28
|
+
| You write the `skip-checks: true` trailer on a handoff-only push, or expect a pull-request body edit to re-run a check | `skill://legion-worker/references/skip-checks-trailer.md` |
|
|
28
29
|
| You review or approve, build the READY packet, or check the merge in production | `skill://legion-worker/references/merge-gate.md` |
|
|
29
30
|
| The issue renames a repository, package, or URL across the codebase | `skill://legion-worker/references/systematic-rename.md` |
|
|
30
31
|
| The issue deletes code, a command, or documentation | `skill://legion-worker/references/cleanup-deletion.md` |
|
|
@@ -282,13 +283,13 @@ and every later phase edits its own lines of the live body rather than replacing
|
|
|
282
283
|
and `proof` array) is the changed behaviour exercised on a production-like surface, recorded as
|
|
283
284
|
its command or run id, what was observed, the head SHA, and one negative control; a unit test is
|
|
284
285
|
never one. **Before you write or edit any line of the PR body or any `proof` array, read
|
|
285
|
-
`skill://legion-worker/references/pr-body.md`**: the template
|
|
286
|
-
|
|
286
|
+
`skill://legion-worker/references/pr-body.md`**: the template, the full definition of a proof,
|
|
287
|
+
what the tester verifies, and the simplify pass.
|
|
287
288
|
|
|
288
289
|
- **The tester runs the application, not CI again.** It drives the changed surface for real and
|
|
289
290
|
tries to break it, judges whether the implementer's tests would catch what it broke, records what
|
|
290
|
-
it ran and saw, and must read CI for everything static — lint, types and the unit
|
|
291
|
-
CI's work, rerun by no tester: `skill://legion-worker/references/pr-body.md`.
|
|
291
|
+
it ran and saw, and must read CI on GitHub for everything static — lint, types and the unit
|
|
292
|
+
suites are CI's work, rerun by no tester: `skill://legion-worker/references/pr-body.md`.
|
|
292
293
|
- **Review threads** are answered and resolved one by one, never in bulk, and never one you have
|
|
293
294
|
not read: one disposition reply per thread, then that thread's own resolution by the
|
|
294
295
|
implementer, the pull request's author — its `gh api graphql` mutation after every push that
|
|
@@ -364,11 +365,12 @@ The daemon reads no handoff file and no branch head; it refuses, before recordin
|
|
|
364
365
|
commit this phase's handoff before completing.
|
|
365
366
|
- each `READY_…` refusal of the merger's READY — named, with its remedy, in the merger's role
|
|
366
367
|
prompt (`skill://legion-worker/references/merge-gate.md` says how READY is checked).
|
|
367
|
-
- `GITHUB_READ_FAILED` — GitHub's failure, not the head's, reading
|
|
368
|
+
- `GITHUB_READ_FAILED` — GitHub's failure, not the head's, reading the pull request's head on
|
|
369
|
+
GitHub: complete again.
|
|
368
370
|
|
|
369
371
|
Such a refusal records nothing, so the corrected call at the same commit is applied. The answer
|
|
370
|
-
may carry a `note` (READY published on an already-merged pull request, or
|
|
371
|
-
|
|
372
|
+
may carry a `note` (READY published on an already-merged pull request, or the
|
|
373
|
+
`MANUAL_MERGE_REQUIRED:` note of a pull request a person merges).
|
|
372
374
|
|
|
373
375
|
Retro's last commit removes `.legion/<issue>/` from the head that merges (`skill://legion-retro`);
|
|
374
376
|
a round after it, such as one a withdrawn READY sends back, writes and commits its own handoff
|
|
@@ -388,28 +390,13 @@ A push whose commits touch only `.legion/` (a handoff-only push) ends its head's
|
|
|
388
390
|
with `skip-checks: true` as the last line, preceded by two empty lines, so GitHub starts no
|
|
389
391
|
workflow run for it and the daemon carries the code head's verdict to it.
|
|
390
392
|
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.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
jj -R "$LEGION_WORKSPACE" describe -r @- --config 'templates.commit_trailers=""' -m "$(jj -R "$LEGION_WORKSPACE" log -r @- --no-graph -T description)"$'\n\n\nskip-checks: true'
|
|
399
|
-
```
|
|
400
|
-
|
|
401
|
-
Every other push — code, red tests, `docs/solutions/`, a forward merge, a rewrite — carries no such
|
|
402
|
-
line and none of GitHub's bracket keywords (`[skip ci]`, `[ci skip]`, `[no ci]`, `[skip actions]`,
|
|
403
|
-
`[actions skip]`), which start no workflow wherever they stand. The daemon classifies a push by the
|
|
404
|
-
paths its commits touch, never by the trailer: a wrong trailer stalls the issue (a trailered code
|
|
405
|
-
push gets no verdict carried; a head with no CI is refused at READY) and never passes it. While the
|
|
406
|
-
head commit carries `skip-checks: true`, a pull-request body edit alone starts no GitHub workflow
|
|
407
|
-
run, so a check that re-judges the body re-runs on that head only after a later push; on a code
|
|
408
|
-
head the same edit re-runs it at once. While the pull request conflicts with its base (GitHub shows
|
|
409
|
-
it `CONFLICTING`), no workflow triggered `on: pull_request` runs for any of its activity types, so
|
|
410
|
-
a body edit re-judges nothing there either; a push cures both, but only once the pull request is
|
|
411
|
-
mergeable, so the implementer forward-merges a conflicting one first (*Reintegrating the base* in
|
|
412
|
-
`skill://legion-worker/references/conflicts-and-rewrites.md`).
|
|
393
|
+
Every other push —
|
|
394
|
+
code, red tests, `docs/solutions/`, a forward merge, a rewrite — carries no such line. The daemon
|
|
395
|
+
classifies a push by the paths its commits touch, never by the trailer: a wrong trailer stalls
|
|
396
|
+
the issue and never passes it. Before you write one, read
|
|
397
|
+
`skill://legion-worker/references/skip-checks-trailer.md`: the shape GitHub honours, the
|
|
398
|
+
`describe` that writes it with trailers off, the bracket keywords banned with it, how a wrong
|
|
399
|
+
trailer stalls the issue, and when a pull-request body edit does and does not start a run.
|
|
413
400
|
|
|
414
401
|
Before the push, inspect `jj -R "$LEGION_WORKSPACE" log -r 'ancestors(@, 5)'` and run the
|
|
415
402
|
identity check above: the chain carries every earlier phase's pushed commits, and pushing them
|
|
@@ -93,7 +93,7 @@ fingerprint=$(set -o pipefail; jj -R "$LEGION_WORKSPACE" diff --from "fork_point
|
|
|
93
93
|
|
|
94
94
|
Where each role gets its two heads: the implementer — the tip before and after its own rebase;
|
|
95
95
|
the tester — the head its `E2E` line names and the new head; the reviewer — the `commit_id` of
|
|
96
|
-
its last submitted review (`gh api repos/{owner}/{repo}/pulls/{n}/reviews --jq '.[] | {commit_id, state, user: .user.login, body}'`, whose body tells a no-fix request that answered a READY refusal from any other)
|
|
96
|
+
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
97
|
and the new head; the merger never computes a fingerprint — it quotes the `--summary` lines
|
|
98
98
|
in READY, and the daemon holds the head to retro's commits.
|
|
99
99
|
|
|
@@ -18,10 +18,10 @@ it, and any other change to the head voids it.
|
|
|
18
18
|
|
|
19
19
|
## The reviewer
|
|
20
20
|
|
|
21
|
-
- The reviewer verifies the `
|
|
21
|
+
- The reviewer reads CI on GitHub and verifies the `Threads` and `E2E` facts there directly —
|
|
22
22
|
never from a handoff — then runs `task(agent="thermonuclear-deep-review")` and
|
|
23
23
|
`task(agent="thermonuclear-code-quality")` once at that head — the head the round reviews —
|
|
24
|
-
and records the verdict. The
|
|
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
26
|
every commit above that push's head changes only `.legion/` (GitHub starts no workflow run for a
|
|
27
27
|
head whose commit message carries `skip-checks: true`, which every handoff-only push before
|
|
@@ -36,22 +36,23 @@ it, and any other change to the head voids it.
|
|
|
36
36
|
```bash
|
|
37
37
|
base=$(gh pr view <n> -R <o>/<r> --json baseRefName -q .baseRefName) && \
|
|
38
38
|
base_path=$(jq -rn --arg b "$base" '$b | split("/") | map(@uri) | join("/")') && \
|
|
39
|
-
echo rulesets: && gh api "repos/<o>/<r>/rules/branches/$base_path" --jq '.[] | select(.type=="required_status_checks") | .parameters.required_status_checks[].context' && \
|
|
39
|
+
echo rulesets: && gh api --paginate "repos/<o>/<r>/rules/branches/$base_path" --jq '.[] | select(.type=="required_status_checks") | .parameters.required_status_checks[].context' && \
|
|
40
40
|
echo protection: && gh api "repos/<o>/<r>/branches/$base_path" --jq '.protection.required_status_checks.checks[].context'
|
|
41
41
|
```
|
|
42
42
|
|
|
43
43
|
A read GitHub refuses stops the command there, and is reported to the architect, never guessed. The CI head, the head of the
|
|
44
44
|
branch's newest `pull_request` run
|
|
45
45
|
(`gh api 'repos/<o>/<r>/actions/runs?branch=<headRefName>&event=pull_request&per_page=30' --jq '.workflow_runs[0].head_sha'`,
|
|
46
|
-
newest first)
|
|
47
|
-
(`gh run view <run-id> --json headSha,status,conclusion`), or the line is stale and the review
|
|
48
|
-
says so. That commit's check-runs
|
|
46
|
+
newest first). That commit's check-runs
|
|
49
47
|
(`gh api repos/<o>/<r>/commits/<ci head>/check-runs --paginate --jq '.check_runs[] | [.name, .status, .conclusion] | @tsv'`):
|
|
50
48
|
every required name present, `completed` and `success` (GitHub counts `neutral` and `skipped`
|
|
51
49
|
as passing for a required check). And the compare proving only `.legion/` changed since
|
|
52
|
-
(`gh api repos/<o>/<r>/compare/<ci head>...<headRefOid> --jq '[.files[].filename] | all(startswith(".legion/"))'`
|
|
53
|
-
prints `true`; `false` means code was pushed since CI ran, so wait for the new
|
|
54
|
-
read the CI head again).
|
|
50
|
+
(`gh api repos/<o>/<r>/compare/<ci head>...<headRefOid> --jq '{handoffsOnly: ([.files[].filename] | all(startswith(".legion/"))), files: (.files | length)}'`
|
|
51
|
+
prints `handoffsOnly` `true`; `false` means code was pushed since CI ran, so wait for the new
|
|
52
|
+
push's runs and read the CI head again). GitHub's compare lists at most 300 files for the whole
|
|
53
|
+
comparison, so `files` at 300 is a comparison GitHub cut at its cap and `handoffsOnly` decides
|
|
54
|
+
nothing: tell the architect and do not approve. A required check
|
|
55
|
+
`queued` or `in_progress`: wait. One `completed` as
|
|
55
56
|
`cancelled`, or absent: `gh run rerun <run-id>` once, then wait; when nothing will run it (a
|
|
56
57
|
check only a commit status reports, a workflow another repository defines), tell the architect
|
|
57
58
|
and do not approve. `gh pr checks <n>` is not the read: it reads only the current head, which
|
|
@@ -148,15 +149,13 @@ reviewing until you finish.
|
|
|
148
149
|
no pre-flight of its own, and its completion reports the commit the workspace stands on. READY's
|
|
149
150
|
checks run in the daemon, which reads the pull request's head on GitHub and refuses, before
|
|
150
151
|
recording anything, on each ground the merger's role prompt names in its `Go daemon workflow`
|
|
151
|
-
part, by its code and with its remedy. That part is the one complete list of READY's refusals.
|
|
152
|
-
A required check or workflow still running refuses nothing; a failed or missing one does. A
|
|
152
|
+
part, by its code and with its remedy. That part is the one complete list of READY's refusals. A
|
|
153
153
|
refusal records nothing; the merger reports it to the architect unless it says to complete
|
|
154
154
|
again, and never pushes a commit to make CI run. On success the daemon posts the packet as a
|
|
155
155
|
`dispatch message` on the issue, publishes it to the project's merge queue role when one is set,
|
|
156
156
|
and says on the issue when that role has no live holder; the answer's `note` says when READY was
|
|
157
|
-
published on a pull request a person already merged, whose head can no longer change, or
|
|
158
|
-
|
|
159
|
-
pull request a person merges.
|
|
157
|
+
published on a pull request a person already merged, whose head can no longer change, or carries
|
|
158
|
+
the by-hand note of a pull request a person merges.
|
|
160
159
|
- **The daemon submits the merge the moment READY is accepted, unless the answer says a person
|
|
161
160
|
merges it.** It queues a `ready_merge` outbox row for the head READY named, and the row, as the
|
|
162
161
|
implement App, reads GitHub and then the record: while the issue still awaits that READY head, a
|
|
@@ -187,7 +186,12 @@ reviewing until you finish.
|
|
|
187
186
|
GitHub's message. While the record still awaits READY's head, GitHub showing the pull request
|
|
188
187
|
closed or at another head is a webhook not yet applied, so the row waits for the two to agree.
|
|
189
188
|
Only once GitHub's last change to the pull request is more than 10 minutes old, however long the
|
|
190
|
-
row waited before, does the row end and tell the architect once (`merge-stalled`). A
|
|
189
|
+
row waited before, does the row end and tell the architect once (`merge-stalled`). A merge the
|
|
190
|
+
daemon submitted that GitHub has not completed within an hour of READY's acceptance tells the
|
|
191
|
+
issue's architect once (`merge-waiting`) and goes on waiting: nothing is withdrawn, the merge
|
|
192
|
+
stands, the issue stays in `awaiting_merge`, and the architect reads the pull request for what
|
|
193
|
+
GitHub waits on — a required check that never ran or failed, a review only a person can give, a
|
|
194
|
+
queue that has not landed it — and asks a person for what no worker can do. A reopen at
|
|
191
195
|
READY's head reuses the row, starts it over as though nothing had been armed, and takes it from a
|
|
192
196
|
run that read the record before the reopen. Any other failure retries.
|
|
193
197
|
No role runs `gh pr merge`. When the answer's `note` begins `MANUAL_MERGE_REQUIRED:` (the pull
|
|
@@ -24,8 +24,6 @@ later phase keeps it current rather than replacing it:
|
|
|
24
24
|
|
|
25
25
|
## Verification
|
|
26
26
|
|
|
27
|
-
**CI:** `Tests` run <run-id> — jobs lint, typecheck, test all success at <head-sha>; `PR Title` run <run-id> — job pr-title success at <head-sha>.
|
|
28
|
-
|
|
29
27
|
**Threads:** <n> resolved, 0 unresolved. Each answered and resolved individually by the implementer, the pull request's author, never in bulk (the reviewer names the bot threads it accepted; it resolves none itself):
|
|
30
28
|
- Thread <node id>: fixed in <commit-sha> — <one line>; resolved at <head-sha>.
|
|
31
29
|
- Thread <node id>: not a defect — <reason>; resolved at <head-sha>.
|
|
@@ -50,14 +48,6 @@ Verified the implementer's proof by <re-running its command | driving the same s
|
|
|
50
48
|
**Chain:** stacked on <base bookmark> frozen at <sha> / not stacked.
|
|
51
49
|
```
|
|
52
50
|
|
|
53
|
-
The `CI` line names the runs GitHub started for the code head, by run id, with each run's state
|
|
54
|
-
as GitHub shows it when the line is written: `success`, a failure, or `queued` / `in progress`
|
|
55
|
-
at <head-sha>, or `no run yet` when GitHub has started none. The implementer writes it at its completion from one `gh run list --commit <head-sha>`
|
|
56
|
-
and never waits for a run to finish to fill it in: the daemon reads every settlement itself, and a
|
|
57
|
-
check or workflow the base branch requires that settles red sends the work back to `implementing`,
|
|
58
|
-
before or after that completion. A later role that reads CI on GitHub updates the line's states to
|
|
59
|
-
what it read.
|
|
60
|
-
|
|
61
51
|
## Never waiting on CI
|
|
62
52
|
|
|
63
53
|
The implementer runs the repository's fast local checks for the paths it changed before the push,
|
|
@@ -117,10 +107,11 @@ and the tester's proof below are both this proof.
|
|
|
117
107
|
application, not CI again. Try to break the changed surface — wrong and boundary inputs, the
|
|
118
108
|
second and the concurrent run, the party it must refuse — judge whether the implementer's tests
|
|
119
109
|
would catch the ways it breaks, writing the red test for one that none would, and
|
|
120
|
-
read CI for everything static: lint, types and the unit
|
|
121
|
-
|
|
122
|
-
`in progress` is
|
|
123
|
-
|
|
110
|
+
read CI on GitHub at the head you are judging for everything static: lint, types and the unit
|
|
111
|
+
and integration suites are CI's, read there and recorded for no later step, and no CI lane is
|
|
112
|
+
rerun as evidence. A lane GitHub shows `queued` or `in progress` is read as such: the tester
|
|
113
|
+
never waits for it, since a required red that settles sends the work back from `testing` and
|
|
114
|
+
the reviewer reads the head's checks before it approves.
|
|
124
115
|
A test handoff whose predecessor carried no proof is a test failure, not a gap for the tester to fill:
|
|
125
116
|
record it in `failures` with `implementerProof.verdict: "rejected"`, complete the phase with
|
|
126
117
|
`verdict: "fail"`, and the daemon returns the issue to the implementer — the agent that developed the change owns
|
|
@@ -149,7 +140,7 @@ and the tester's proof below are both this proof.
|
|
|
149
140
|
the pull request's own diff at that head: nothing applied leaves the head as it is; applied →
|
|
150
141
|
the implementer re-runs the repository's checks and its own proof on the applied head for the
|
|
151
142
|
surface the simplify diff touched, since a refactor that "preserves behaviour" is a claim until
|
|
152
|
-
it is executed, and
|
|
143
|
+
it is executed, and updates the `E2E (implementer)` line to that head before it completes.
|
|
153
144
|
That cost is why 0-applied is the expected outcome and a pass that applies is spent sparingly.
|
|
154
145
|
The reviewer's pair runs at the head each round reviews. Record both in the `Thermo` line.
|
|
155
146
|
- **No deferrals** is the body's rule (*PR body, review, and the merge gate* in
|
|
@@ -36,9 +36,12 @@ Three facts govern every thread, whoever opened it.
|
|
|
36
36
|
it accepted to the implementer, who resolves them. List the pull request's threads:
|
|
37
37
|
|
|
38
38
|
```bash
|
|
39
|
-
gh api graphql -f query='query($o:String!,$r:String!,$n:Int
|
|
39
|
+
gh api graphql --paginate -f query='query($o:String!,$r:String!,$n:Int!,$endCursor:String){repository(owner:$o,name:$r){pullRequest(number:$n){reviewThreads(first:100,after:$endCursor){pageInfo{hasNextPage endCursor} nodes{id isResolved comments(last:1){nodes{author{login} body}}}}}}}' -F o=<owner> -F r=<repo> -F n=<number>
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
`--paginate` follows the thread pages, which it prints as one JSON object per page, so add
|
|
43
|
+
`--slurp` when you want a single document; `comments(last:1)` needs no paging of its own.
|
|
44
|
+
|
|
42
45
|
Reply on each thread you answer, then tell the implementer the node ids of exactly the threads
|
|
43
46
|
you accepted — a bot's findings you adjudicated — never one you have not read: an
|
|
44
47
|
`envoy_publish` to the implementer's role topic (it is live on the issue and answers while you
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# The `skip-checks: true` trailer, and what starts a workflow run
|
|
2
|
+
|
|
3
|
+
Part of `skill://legion-worker`. Read it before you write the trailer on a handoff-only push, and
|
|
4
|
+
whenever you expect a pull-request body edit to re-run a check. `skill://legion-worker/SKILL.md`
|
|
5
|
+
carries the rule — which push carries the trailer, which does not, and that a wrong trailer stalls
|
|
6
|
+
the issue; this is its shape, its command, and its consequences. Every path it cites is in
|
|
7
|
+
sjawhar/legion.
|
|
8
|
+
|
|
9
|
+
## The shape GitHub honours
|
|
10
|
+
|
|
11
|
+
A push whose commits touch only `.legion/` (a handoff-only push) ends its head's commit message
|
|
12
|
+
with `skip-checks: true` as the last line, preceded by two empty lines, so GitHub starts no
|
|
13
|
+
workflow run for it and the daemon carries the code head's verdict to it — except retro's push,
|
|
14
|
+
which never carries the line whatever paths it touches, for the reason the next section gives.
|
|
15
|
+
GitHub honours the
|
|
16
|
+
trailer only in that shape — the last line, after two empty lines; one empty line is not enough,
|
|
17
|
+
and GitHub then starts every workflow as if the line were not there (a planner's handoff push did
|
|
18
|
+
exactly that in the first live run of this rule). jj keeps the two lines as written, where
|
|
19
|
+
`git commit`'s default cleanup would collapse them. Write the line with trailers off for the one
|
|
20
|
+
describe, since the pane's jj overlay would otherwise append `Omp-Session:` after it:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
jj -R "$LEGION_WORKSPACE" describe -r @- --config 'templates.commit_trailers=""' -m "$(jj -R "$LEGION_WORKSPACE" log -r @- --no-graph -T description)"$'\n\n\nskip-checks: true'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Every other push carries no such line
|
|
27
|
+
|
|
28
|
+
Every other push — code, red tests, `docs/solutions/`, a forward merge, a rewrite — carries no
|
|
29
|
+
such line and none of GitHub's bracket keywords (`[skip ci]`, `[ci skip]`, `[no ci]`,
|
|
30
|
+
`[skip actions]`, `[actions skip]`), which start no workflow wherever they stand.
|
|
31
|
+
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.
|
|
32
|
+
That is sjawhar/legion#2143's sentence, and `packages/pi-legion/src/skills-guard.test.ts` counts
|
|
33
|
+
it here and in the four other texts that state it, so none of the five can drift from the rest.
|
|
34
|
+
The daemon classifies a push by the paths its commits touch, never by the
|
|
35
|
+
trailer: a wrong trailer stalls the issue (a trailered code push gets no verdict carried, and a
|
|
36
|
+
head with no CI is one a base that requires a check will not merge, so the merge waits and the
|
|
37
|
+
daemon tells the issue's architect once it has waited an hour) and never passes it.
|
|
38
|
+
|
|
39
|
+
## When a body edit re-triggers a workflow, and when it does not
|
|
40
|
+
|
|
41
|
+
While the head commit carries `skip-checks: true`, a pull-request body edit alone starts no GitHub
|
|
42
|
+
workflow run, so a check that re-judges the body re-runs on that head only after a later push; on
|
|
43
|
+
a code head the same edit re-runs it at once. While the pull request conflicts with its base
|
|
44
|
+
(GitHub shows it `CONFLICTING`), no workflow triggered `on: pull_request` runs for any of its
|
|
45
|
+
activity types, so a body edit re-judges nothing there either; a push cures both, but only once
|
|
46
|
+
the pull request is mergeable, so the implementer forward-merges a conflicting one first
|
|
47
|
+
(*Reintegrating the base* in `skill://legion-worker/references/conflicts-and-rewrites.md`).
|