@sjawhar/pi-legion 10.3.4 → 10.3.6

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
@@ -14452,7 +14452,7 @@ var dispatchToolSpecs = [
14452
14452
  status: "done",
14453
14453
  reason: "Shipped in owner/repo#7; verified on the production dashboard."
14454
14454
  },
14455
- description: "Update an existing issue: move its lifecycle status, retitle it, replace its labels, set " + "its priority, link a URL (the pull request that delivers it, a run, a document), set its " + "route, set or clear its parent, replace the issues it is blocked by, or attach it to " + "architecture components. Status is one of " + `${ISSUE_STATUSES.join(", ")}; outside Legion, move it yourself as the work advances; inside ` + "Legion the daemon moves it. external_links are " + "merged into the issue's existing links by URL, so linking the pull request you just opened " + "keeps every earlier link. components replaces the issue's own attachment. Closing an issue " + "(status done) requires reason, the note that says why: it is posted on the issue as a " + "message, then the issue closes, because a closed issue refuses messages, comments, and " + "artifacts; reason goes only with status done. A closed issue takes only rank, components, " + "and a reopening status (any status but done); everything else, priority included, waits " + "for the reopen. " + "priority is yours to set and a human overrides it; rank, the board's own order, is not " + "settable here. At least one " + `field besides issue is required. ${ISSUE_REFERENCE}`,
14455
+ description: "Update an existing issue: move its lifecycle status, retitle it, replace its labels, set " + "its priority, link a URL (the pull request that delivers it, a run, a document), set its " + "route, set or clear its parent, replace the issues it is blocked by, or attach it to " + "architecture components. Status is one of " + `${ISSUE_STATUSES.join(", ")}; outside Legion, move it yourself as the work advances; inside ` + "Legion the daemon moves it. A non-empty external_links is " + "merged into the issue's existing links by URL, so linking the pull request you just opened " + "keeps every earlier link; an empty external_links removes every link instead. components " + "replaces the issue's own attachment. Closing an issue " + "(status done) requires reason, the note that says why: it is posted on the issue as a " + "message, then the issue closes, because a closed issue refuses messages, comments, and " + "artifacts; reason goes only with status done. A closed issue takes only rank, components, " + "a reopening status (any status but done), and an external_links write that adds no new " + "link (so a link can move to the issue that replaced it); everything else, priority " + "included, waits for the reopen. " + "priority is yours to set and a human overrides it; rank, the board's own order, is not " + "settable here. At least one " + `field besides issue is required. ${ISSUE_REFERENCE}`,
14456
14456
  arguments: (z2) => ({
14457
14457
  issue: z2.string().describe(ISSUE_REFERENCE),
14458
14458
  status: z2.enum(ISSUE_STATUSES).describe("New lifecycle status.").optional(),
@@ -14460,7 +14460,7 @@ var dispatchToolSpecs = [
14460
14460
  title: z2.string({ min: 1, max: ISSUE_TITLE_MAX }).describe("Replacement title, at most 1,000 characters.").optional(),
14461
14461
  labels: z2.array(z2.string({ min: 1, max: 40 }), { max: 20 }).describe("Replacement label set, at most 20 labels of up to 40 characters; replaces every existing label.").optional(),
14462
14462
  priority: z2.number({ int: true, min: 0, max: 3 }).nullable().optional().describe("Coarse priority: 0 is P0 (highest) through 3 is P3 (lowest); null clears it."),
14463
- external_links: z2.array(z2.string({ min: 1 })).describe("URLs to link; merged into the issue's existing external links by URL.").optional(),
14463
+ external_links: z2.array(z2.string({ min: 1 })).describe("URLs to link; a non-empty list merges into the issue's existing external links by URL, an empty list removes every link instead.").optional(),
14464
14464
  route: z2.string().describe("Route the issue to role:<name> or session:<id>; an empty string clears it.").optional(),
14465
14465
  parent: z2.string().describe("Parent issue key in the same project; an empty string clears the parent.").optional(),
14466
14466
  blocked_by: z2.array(z2.string({ min: 1 }), { max: MAX_ISSUE_BLOCKERS }).describe("Replacement list of issue keys in the same project this issue waits on; [] clears it. Dispatch refuses a dependency cycle.").optional(),
@@ -16148,7 +16148,7 @@ function onEnvoyRoleRegained(listener) {
16148
16148
  // package.json
16149
16149
  var package_default = {
16150
16150
  name: "@sjawhar/pi-legion",
16151
- version: "10.3.4",
16151
+ version: "10.3.6",
16152
16152
  license: "Apache-2.0",
16153
16153
  type: "module",
16154
16154
  omp: {
@@ -315,12 +315,14 @@ The daemon keeps this order from its fixed table; you start none of its steps:
315
315
  refuses a head that still carries `.legion/<issue>/`; the daemon posts
316
316
  `READY #<n> at <current sha> (approved at <approved sha>) for <KEY> (<pr url>)` on the Dispatch
317
317
  issue and publishes it to the project's merge queue role when one is set. The moment the daemon
318
- accepts READY, the merger submits the pull request with
319
- `gh pr merge <n> -R <owner>/<repo> --auto --squash --match-head-commit <head>` and tells you what
320
- the command answered; the repository's required reviews and checks decide when it lands, and
321
- Legion neither reads nor writes those rules. A READY the design gate refused is submitted later:
322
- the merger submits nothing on a refusal, so once that spec version is approved and the daemon
323
- posts the packet, tell the merger to submit it. If the merger reports a failed verification,
318
+ accepts READY, it submits the merge of that head itself, as the implement App and with GitHub's
319
+ expected-head check: a merge queue enqueues it, a pull request already mergeable merges at once,
320
+ a repository that allows auto-merge arms it, and in one that allows neither the daemon merges it
321
+ once GitHub reports it mergeable. The repository's required reviews and checks decide when it
322
+ lands, and Legion neither reads nor writes those rules; a review only a person can give is
323
+ waited for, never bypassed, and once given the daemon merges or the person does. A READY the
324
+ design gate refused is submitted by the daemon once that spec version is approved; nobody is
325
+ told to submit it. If the merger reports a failed verification,
324
326
  treat it like `pr-blocked`: the merger holds the phase, so tell it to move the issue back with
325
327
  `request_backward_move`, naming what failed; never bypass. A READY refused because the head
326
328
  still carries `.legion/<issue>/` goes back to `retro`, so the implementer's retro removes it.
@@ -404,6 +406,8 @@ active phase worker.
404
406
  | `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`, `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 `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 either a conflict (`the head conflicts with <base>`) or the required checks that failed (`CI is red at <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. 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) are the work going in circles: narrow it, re-plan it with the responsible worker, or ask a human with `dispatch ask`. 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` because its task names no approved head is not the work going in circles either. A move that sends back code found wrong counts with the tester fails. |
405
407
  | `pr-merged` | Its `reason` names the pull request. The PR merged, under the repository's rules, before the issue reached `awaiting_merge`. The workflow runs on and asks no one to merge it: the daemon starts the **implementer** on the production check once the issue gets there, and you start nothing. Its `phase-finished` for `production_check` is what brings you to step 7: verify the record on the pull request and this issue first, then sign off naming it with `sign_off`. A merge is not the close. |
406
408
  | `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. |
409
+ | `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 moves the issue on to its production check. Never bypass a rule. |
410
+ | `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 moves the issue on to its production check. 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. |
407
411
  | 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. |
408
412
  | `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. |
409
413
  | `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. |
@@ -30,7 +30,7 @@ retrospective's durable output.
30
30
  `jj diff --from <approved-sha> --to <tip-sha> --summary` in READY, and sends the READY packet
31
31
  with its completion, which refuses a head that still carries `.legion/<issue>/`; the daemon
32
32
  posts it on the Dispatch issue and publishes it to the project's merge-queue role when one is
33
- configured. On READY's acceptance the merger submits the merge, and it lands under the
33
+ configured. On READY's acceptance the daemon submits the merge, and it lands under the
34
34
  repository's rules: its required reviews and checks, and its merge queue when it enables one.
35
35
  5. After that merge, the implementer — not the reviewer or merger — verifies the change in production
36
36
  and records it on the PR and the issue: the agent that developed it is responsible for testing
@@ -307,12 +307,11 @@ line), the full definition of a proof, what the tester verifies, and the simplif
307
307
  afterwards is in the same reference: `skill://legion-worker/references/conflicts-and-rewrites.md`.
308
308
  - **The merge gate**, in order: the tester's evidence green → the reviewer's approval of the head
309
309
  → retro → the merger's READY → the merge → the implementer's production check. On READY's
310
- acceptance the merger submits the pull request with
311
- `gh pr merge <n> -R <owner>/<repo> --auto --squash --match-head-commit <head>`, unless the
312
- answer's note begins `MANUAL_MERGE_REQUIRED:`, where a person merges it; the repository's
313
- required reviews and checks decide when it lands
310
+ acceptance the daemon submits the merge of the head READY named, unless the answer's note begins
311
+ `MANUAL_MERGE_REQUIRED:`, where a person merges it; no role runs `gh pr merge`, and the
312
+ repository's required reviews and checks decide when it lands
314
313
  (`skill://legion-worker/references/merge-gate.md`). The reviewer's submissions, retro's commit,
315
- the merger's READY and its submission, and the production check follow the same reference.
314
+ the merger's READY, the daemon's submission, and the production check follow the same reference.
316
315
  - **No surface reaches the changed path** is a report to the architect, never a reason to
317
316
  complete the phase: `skill://legion-worker/references/pr-body.md`.
318
317
 
@@ -1,8 +1,8 @@
1
1
  # The merge gate: review, retro, READY, and the production check
2
2
 
3
3
  Part of `skill://legion-worker`. Read it when you are the reviewer submitting a review or an
4
- approval, the implementer recording the production check, or the merger building READY and
5
- submitting the merge. Every path it cites is in sjawhar/legion.
4
+ approval, the implementer recording the production check, or the merger building READY. Every
5
+ path it cites is in sjawhar/legion.
6
6
 
7
7
  The order, in full: the tester's evidence green → the reviewer's approval of the head →
8
8
  retro → the merger's READY → the merge → the implementer's production check. The approved
@@ -126,44 +126,57 @@ reviewing until you finish.
126
126
  checks run in the daemon, which reads the pull request's head on GitHub and refuses, before
127
127
  recording anything, on each ground the merger's role prompt names in its `Go daemon workflow`
128
128
  part, by its code and with its remedy. That part is the one complete list of READY's refusals.
129
- A refusal records nothing; the merger reports it to the architect unless it says to complete
129
+ A required check or workflow still running refuses nothing; a failed or missing one does. A
130
+ refusal records nothing; the merger reports it to the architect unless it says to complete
130
131
  again, and never pushes a commit to make CI run. On success the daemon posts the packet as a
131
132
  `dispatch message` on the issue, publishes it to the project's merge queue role when one is set,
132
133
  and says on the issue when that role has no live holder; the answer's `note` says when READY was
133
134
  published on a pull request a person already merged, whose head can no longer change, or on a
134
135
  base that requires no check, which the packet itself also says, or carries the by-hand note of a
135
136
  pull request a person merges.
136
- - **The merger submits the merge the moment READY is accepted, unless the answer says a person
137
- merges it.** In the same turn, before anything else, it runs
138
- `gh pr merge <n> -R <owner>/<repo> --auto --squash --match-head-commit <head>` with its pane's
139
- plain `gh`: `<n>` the pull request number, `<head>` the full sha of the current head READY named.
140
- A repository with a merge queue enqueues the pull request; one without arms auto-merge; one whose
141
- rules are already satisfied merges at once. `--match-head-commit` has GitHub refuse the request
142
- when the head has moved since READY read it. The repository's required reviews and checks decide
143
- when it lands: the merger makes no merge by hand and bypasses no rule. It then tells the
144
- architect with `envoy_publish` to its encoded role token what the command answered — enqueued,
145
- auto-merge enabled, or merged — and the head. When the answer's `note` begins
146
- `MANUAL_MERGE_REQUIRED:` (the pull request touches a path the project's `merge.by_hand` names),
147
- READY stands but the merger submits nothing: it tells the architect the note and the head, and a
148
- person merges under the repository's rules. The daemon tells that READY with the note at its head
149
- and the packet's `Merge:` line replaced, so the issue and the merge queue role are never asked to
150
- arm it. On any READY refusal it submits nothing, since an armed request would merge through the
151
- gate the refusal names; a READY the design gate refused is submitted later, on the architect's
152
- word, once the gate opens, and that turn runs only `gh` and a read of the READY the daemon posted
153
- then: a refusal or a posted READY carrying the by-hand note is submitted by nobody. A push that
154
- moves the head past a READY the gate holds voids it before then: the architect is told
155
- `READY_HEAD_MOVED` and has the merger build a new READY at the new head. A
156
- `gh pr merge` GitHub refuses is reported to the architect with its message; a person can still
157
- merge after READY. The packet's second line records the step,
158
- `Merge: submit on normal acceptance with gh pr merge <n> -R <owner>/<repo> --auto --squash --match-head-commit <head>`.
159
- - **The daemon disarms a merge no READY covers.** Auto-merge stays enabled across pushes, and
160
- `--match-head-commit` is checked only when it is enabled, so the daemon itself disables the pull
137
+ - **The daemon submits the merge the moment READY is accepted, unless the answer says a person
138
+ merges it.** It queues a `ready_merge` outbox row for the head READY named, and the row, as the
139
+ implement App, reads GitHub and then the record: while the issue still awaits that READY head, a
140
+ base with a merge queue gets the pull request enqueued; otherwise a pull request GitHub reports
141
+ mergeable now (`CLEAN`, `UNSTABLE` or `HAS_HOOKS`) is squash-merged, and one in a repository that
142
+ allows auto-merge is armed. Every request carries READY's head as GitHub's expected head, so
143
+ GitHub refuses it once the head has moved. In a repository that allows neither, the row waits,
144
+ reading GitHub again on the outbox's backoff, until GitHub reports the pull request mergeable —
145
+ a required check finishing, a required review a person gives — and then merges it. An arm or a
146
+ queue entry is not the end: the row keeps reading GitHub until GitHub reports the pull request
147
+ merged at READY's head, and submits again when the arm or entry is gone while the record still
148
+ awaits READY (a disarm sent before READY that GitHub applied late). GitHub's
149
+ rules decide each answer: the daemon bypasses none, and a person may merge it first. A refusal a
150
+ retry would meet again (squash merges disabled, a permission the App lacks) ends the row and is
151
+ told to the architect once (`merge-refused`), with GitHub's message. While the record still
152
+ awaits READY's head, GitHub showing the pull request closed or at another head is a webhook not
153
+ yet applied, so the row waits for the two to agree. Only once GitHub's last change to the pull
154
+ request is more than 10 minutes old, however long the row waited before, does the row end and
155
+ tell the architect once (`merge-stalled`). A reopen at READY's head reuses the row and takes it
156
+ from a run that read the record before the reopen. Any other failure retries.
157
+ No role runs `gh pr merge`. When the answer's `note` begins `MANUAL_MERGE_REQUIRED:` (the pull
158
+ request touches a path the project's `merge.by_hand` names), READY stands but the daemon submits
159
+ nothing: the merger tells the architect the note and the head, and a person merges under the
160
+ repository's rules. While an earlier auto-merge disarm for that pull request remains unfinished,
161
+ a by-hand READY refuses `READY_DISARM_PENDING`; retry it once the disarm finishes, so a stale
162
+ ordinary arm cannot merge the person-only head. Ordinary READY does not wait for the disarm. The
163
+ daemon tells that READY with the note at its head and the packet's `Merge:` line replaced, so the
164
+ issue and the merge queue role are never asked to arm it. A READY the design gate refused is
165
+ submitted by the daemon when the approval that opens the gate moves the issue to
166
+ `awaiting_merge`. A push that moves the head past a READY the gate holds voids it before then:
167
+ the architect is told `READY_HEAD_MOVED` and has the merger build a new READY at the new head.
168
+ The packet's second line records the step,
169
+ `Merge: the daemon submits pull request #<n> at <head> once READY is accepted`.
170
+ - **The daemon disarms a merge no READY covers.** Auto-merge stays enabled across pushes, and the
171
+ expected head is checked only when it is enabled, so the daemon itself disables the pull
161
172
  request's auto-merge and takes it out of a merge queue, as the implement App, on any push or
162
173
  reopen but one landing on an awaiting READY's head, when READY is withdrawn (a push or reopen
163
174
  moved the head past it, its CI turned red, or it conflicts with its base, each of which sends the
164
175
  issue back to `implementing`), when a child is set back to `todo`, and when the issue leaves the
165
- workflow. A by-hand READY covers no arm, so whoever arms it is undone. READY waits
166
- (`READY_DISARM_PENDING`) until that disarm has finished. No role runs a disarm.
176
+ workflow. A by-hand READY covers no arm, so whoever arms it is undone. A `ready_merge` row whose
177
+ READY the record has left, or whose pull request the record holds closed, submits nothing; a pull
178
+ request reopened at an awaiting READY's head gets that head's merge submitted again. No role runs
179
+ a disarm.
167
180
 
168
181
  ## After the merge
169
182
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion",
3
- "version": "10.3.4",
3
+ "version": "10.3.6",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "omp": {