@sjawhar/pi-legion 10.23.0 → 10.24.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 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.23.0",
16940
+ version: "10.24.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` | Its `reason` names the pull request, READY's head, what the daemon asked GitHub to do (merge, enqueue, or enable auto-merge for it), and GitHub's refusal, which a retry would meet again (squash merges disabled, a permission the implement App lacks). The daemon has stopped submitting that merge; the issue stays in `awaiting_merge`. The cause is a repository setting or an App permission, a person's to change: ask one with `dispatch ask`, naming the refusal and the pull request at READY's head, which that person merges under the repository's rules once it is fixed. The merge closes the issue. Never bypass a rule. |
24
- | `merge-stalled` | Its `reason` names the pull request, the state and head GitHub shows (closed, or another head), since when GitHub has shown it, and READY's head, which the record still awaits. GitHub's last change to the pull request is more than 10 minutes old and the record never heard of it, so its webhook never reached the daemon: the daemon has stopped submitting that merge, and the issue stays in `awaiting_merge`. Read the pull request on GitHub. Open at READY's head, GitHub has caught up: a person merges it under the repository's rules, and the merge closes the issue. Closed, or at another head the daemon never recorded: ask a person with `dispatch ask`, naming the pull request, what GitHub shows, and READY's head. Never bypass a rule. |
25
- | `merge-removed` | Its `reason` names who took the pull request out of the merge queue or turned its auto-merge off at READY's head after the daemon submitted it, or says GitHub turned it off on its own (over a repository setting such as a merge method the repository blocks) and names the App that submitted the merge, which GitHub names as the actor of its own disables; then what was removed, GitHub's reason (`manual`, `merge_conflict`, `manually_disabled`, `merge_commit_blocked`, …), when, and READY's head. It was not a failed merge, which comes as a `checks-red` instead. The daemon submits that merge no further, so the removal stands, and the issue stays in `awaiting_merge`. Read the pull request on GitHub. A `merge_conflict` conflicts with the base or with a pull request queued ahead of it: once the conflict is on the base, the daemon's own conflict check sends the issue back to `implementing` by itself. Otherwise ask a person with `dispatch ask` (the one who removed it, when the reason names a person rather than GitHub on its own), naming the pull request at READY's head and the reason; that person merges or queues it again under the repository's rules, and the merge closes the issue. Never bypass a rule. |
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 rather
137
- than rewriting it, and READY refuses a head whose required check failed, so the tree stops at the
138
- merger.
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 (the sole definition of the CI
286
- line), the full definition of a proof, what the tester verifies, and the simplify pass.
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 suites are
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 READY's checks: complete again.
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 a base requiring no
371
- check, or the `MANUAL_MERGE_REQUIRED:` note of a pull request a person merges).
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
- GitHub honours the trailer only in that shape — the last line, after two empty lines; one empty line
392
- is not enough, and GitHub then starts every workflow as if the line were not there (a planner's
393
- handoff push did exactly that in the first live run of this rule). jj keeps the two lines as
394
- written, where `git commit`'s default cleanup would collapse them. Write the line with trailers off
395
- for the one describe, since the pane's jj overlay would otherwise append `Omp-Session:` after it:
396
-
397
- ```bash
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 `CI`, `Threads`, and `E2E` facts against GitHub directly —
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 `CI` fact is the checks that stand for the code under review:
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); the PR body's `CI:` line names runs at that same commit
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 push's runs and
54
- read the CI head again). A required check `queued` or `in_progress`: wait. One `completed` as
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 on a
158
- base that requires no check, which the packet itself also says, or carries the by-hand note of a
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 reopen at
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 and integration suites are CI's, cited
121
- from the `CI` line, and no CI lane is rerun as evidence. A lane the line shows `queued` or
122
- `in progress` is cited as such: the tester never waits for it, since a required red that settles
123
- sends the work back from `testing` and the reviewer reads the head's checks before it approves.
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 re-cites the `CI` line and `E2E (implementer)` there before it completes.
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!){repository(owner:$o,name:$r){pullRequest(number:$n){reviewThreads(first:100){nodes{id isResolved comments(last:1){nodes{author{login} body}}}}}}}' -F o=<owner> -F r=<repo> -F n=<number>
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`).
@@ -13,8 +13,11 @@ which you have; those need no runner.
13
13
 
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
- dispatch:local`, `packages/dispatch/e2e/local-instance.ts`) refuses to start without it on
17
- `PATH` — so a role whose change owes that suite can run it.
16
+ dispatch:local`, `packages/dispatch/e2e/local-instance.ts`) refuses to start without `go`,
17
+ `psql`, `flock` and `openssl` on `PATH`, and without `docker` too unless a `DATABASE_URL` is
18
+ given. The image carries `go`, `flock` and `openssl`, a `DATABASE_URL` stands in for `docker`,
19
+ and `psql` was the one a role had no way to supply — so a role whose change owes that suite can
20
+ run it.
18
21
  - `redis-cli` — no repository of the project speaks Redis today; it is carried anyway, at about a
19
22
  megabyte, because a second image roll costs more than a megabyte.
20
23
  - `sqlite3` — a repository's own SQLite fixture or database has no other way to open from a shell,
@@ -88,8 +91,10 @@ variable the runtime sets for you there or under a path on that volume your harn
88
91
  itself — but outside the issue's workspace (`$LEGION_WORKSPACE`, under `/legion/workspaces`) and
89
92
  the runtime's own directories (`/legion/repos`, `/legion/feed`, `/legion/sessions`); and after
90
93
  the command exits nothing else resolves differently. The workspace is excluded because its jj
91
- working copy tracks every new file, so a root written there is added to the change every role
92
- shares; the one write into it that is sanctioned is a repository's own dependency install into
94
+ working copy tracks every new file under jj's `snapshot.max-new-file-size` (1 MiB in a role
95
+ container), so a root written there is added to the change every role shares, and a larger new
96
+ file is left untracked, every jj command in the workspace then warning `Refused to snapshot some
97
+ files`. The one write into it that is sanctioned is a repository's own dependency install into
93
98
  its ignored directories, below. The variables the runtime sets on the volume, where a tool's own
94
99
  cache already goes, are
95
100
  `XDG_CACHE_HOME` (`/legion/scratch/cache`), `XDG_DATA_HOME`, `XDG_STATE_HOME`, `TMPDIR`, `GOCACHE`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion",
3
- "version": "10.23.0",
3
+ "version": "10.24.0",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "omp": {