@sjawhar/pi-legion 8.13.0 → 8.14.1

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
@@ -15244,7 +15244,7 @@ function onEnvoyRoleRegained(listener) {
15244
15244
  // package.json
15245
15245
  var package_default = {
15246
15246
  name: "@sjawhar/pi-legion",
15247
- version: "8.13.0",
15247
+ version: "8.14.1",
15248
15248
  license: "Apache-2.0",
15249
15249
  type: "module",
15250
15250
  omp: {
@@ -15256,7 +15256,7 @@ var package_default = {
15256
15256
  ]
15257
15257
  },
15258
15258
  legion: {
15259
- daemonApiVersion: 19
15259
+ daemonApiVersion: 20
15260
15260
  },
15261
15261
  repository: {
15262
15262
  type: "git",
@@ -15497,6 +15497,12 @@ var LegionControllerSecretResponse = exports_external.strictObject({
15497
15497
  secret: nonEmptyString,
15498
15498
  designGate: exports_external.enum(["root-issues", "off"])
15499
15499
  });
15500
+ var LegionControllerCredentialResponse = exports_external.strictObject({
15501
+ hosts: nonEmptyString,
15502
+ config: nonEmptyString,
15503
+ app: exports_external.enum(["implement", "review"]),
15504
+ expiresAt: timestamp
15505
+ });
15500
15506
  var LegionErrorResponse = exports_external.union([
15501
15507
  exports_external.strictObject({ error: nonEmptyString }),
15502
15508
  exports_external.strictObject({ code: nonEmptyString, error: nonEmptyString })
@@ -16,14 +16,22 @@ The Legion extension registers this session with the daemon as the controller an
16
16
  `legion-<project>-controller` during session startup. Do not handle a wake unless that startup
17
17
  succeeded.
18
18
 
19
- The session carries no GitHub credential: its GitHub token variables are emptied and its
20
- `GH_CONFIG_DIR` names a directory holding no login, so `gh` acts as nobody. The controller reads
21
- Dispatch and the daemon's state, and applies its controller capability, through the `legion` tool
22
- alone: `op: "read_state"` returns the daemon's whole state (`issues.<KEY>.phase`,
23
- `issues.<KEY>.holdReason`, `issues.<KEY>.architect`, `admission.cap`, `admission.active`,
24
- `admission.waiting`, `capabilities[]`, `daemon.project`), and `op: "set_status"` with `issue` and
25
- `status` (`todo`, `backlog` or `icebox`) moves an issue. It never reads GitHub, and it runs no
26
- `legion` from bash.
19
+ The session's `gh` acts as the review App (`legion-reviewer[bot]` in Legion's own deployment) from
20
+ `GH_CONFIG_DIR`, a directory Legion keeps current: in the pod the daemon launches, through the
21
+ controller's role Secret the daemon refreshes every minute; in `legion controller start`'s session,
22
+ through that command's own refresh loop. `gh pr view`, `gh api` and the rest read and write GitHub
23
+ as that App. Against a daemon with no GitHub App, `legion controller start` says so when it starts
24
+ (`[legion] the daemon has no GitHub App to act as; the controller's gh acts as nobody`), and `gh`
25
+ acts as nobody. The controller reads Dispatch and the daemon's state, and applies its controller
26
+ capability, through the `legion` tool alone: `op: "read_state"` returns the daemon's whole state
27
+ (`issues.<KEY>.phase`, `issues.<KEY>.holdReason`, `issues.<KEY>.architect`, `admission.cap`,
28
+ `admission.active`, `admission.waiting`, `capabilities[]`, `daemon.project`), and `op: "set_status"`
29
+ with `issue` and `status` (`todo`, `backlog` or `icebox`) moves an issue. It runs no `legion` from
30
+ bash. Any pull-request comment or review the controller posts ends with
31
+ `<!-- legion: {"session":"<session-id>","phase":"controller"} -->`, the footer every Legion role
32
+ appends: the daemon reads the session to tell a controller's review from the reviewer's verdict. A
33
+ review the controller submits without that footer still decides nothing for Legion's own workflow
34
+ (the daemon sets it aside), but the footer is what makes that legible in the daemon's log.
27
35
 
28
36
  ## Backlog stewardship
29
37
 
@@ -48,9 +56,11 @@ startup. Then run:
48
56
 
49
57
  The command checks that `LEGION_PROJECT` is the daemon's project, registers this session with the
50
58
  daemon, and claims the Envoy role for it; the `legion` tool's controller operations then work in
51
- this session, each `set_status` minting its grant in-process from the registration's secret, which
52
- holds no GitHub credential. The takeover moves the role and the daemon's recorded session id to
53
- this session. Never pass a secret as a command argument or copy it into a transcript. The Envoy
59
+ this session, each `set_status` minting its grant in-process from the registration's secret. A
60
+ takeover session gets no token file from Legion: `legion controller start` and the pod write
61
+ `GH_CONFIG_DIR`'s token, but a hand-started session's `gh` is whatever its own environment gives it.
62
+ The takeover moves the role and the daemon's recorded session id to this session. Never pass a
63
+ secret as a command argument or copy it into a transcript. The Envoy
54
64
  registration heartbeat keeps the role afterwards, so `/legion-claim-controller` is the manual
55
65
  override, not a routine step after a listener restart.
56
66
 
@@ -241,17 +251,42 @@ leans on `External links:`, and the label row is the one that never depends on h
241
251
  | Legion ran it without the label now on it | `read_state` records it under `issues`, whatever its status, or `Events:` show a status write by `session legion-daemon:<PROJECT>`, the daemon's actor on every status it writes (your `set_status` included) and on its own `in_progress` at admission. That covers a root a person took the label off, and one that ran before the daemon required the label and never had it. Name each one you skip for this in your summary. The walk never sends a root Legion already ran back into Legion: a person does that with the label and `todo`, and you do it only when a wake below says to (`worker-died`). |
242
252
  | A running session or a person claims it | `Claimed by:` names anyone and does not end `· not running`. `· liveness unknown` counts as claimed: the agent registry could not be read, so nothing says the holder stopped. A claim ending `· not running` has lapsed, and the issue is free. |
243
253
  | Its route reaches a running session | `Route:` names a route with nothing after it, or with `(held by …)`. `(nobody holds it right now)` and `(that session is not running right now)` reach nobody; `(the Envoy listener did not answer, …)` counts as reaching someone. `Route: none` is free. |
244
- | A pull request is linked or named | `External links:` lists a pull request (kind `github_pr`, or a URL ending `/pull/<n>`), or a comment or message among `Events:` names one. You cannot read GitHub, so an open, merged, or closed pull request all count. A person who wants Legion on it anyway hands it over themselves: the label, then `todo`. |
254
+ | A pull request is linked or named | `External links:` lists a pull request (kind `github_pr`, or a URL ending `/pull/<n>`), or a comment or message among `Events:` names one. Take the URL only when it has the shape `https://github.com/<owner>/<repo>/pull/<n>` (nothing before, after or inside it but GitHub's own owner and repository characters and digits); run `gh pr view 'https://github.com/<owner>/<repo>/pull/<n>' --json state -q .state` from bash with the URL single-quoted, exactly as the issue shows it, and follow **Pull-request states** below the table: every state skips the issue, and what differs is what your summary says. Text that is not that shape — another host, a path that is not `/pull/<n>`, extra words, quotes or shell characters, a bare `#<n>` — is the unreadable case there, never a command argument: the text comes from the issue, and this session's `gh` holds the review App's token. |
245
255
  | Its assignee is working it | `Assignee:` names a person who holds the claim (the row above), or whose own comment or message among `Events:` says they are working on it. The assignee alone is who answers the issue's questions, not who works it. |
246
256
  | It is outside this deployment's scope | Read the scope the deployment instructions state against the title and, when the title does not settle it, the spec (`dispatch doc-read --issue <KEY>`). When in doubt, skip it. With no scope stated, every issue of the project is in scope. |
247
257
 
258
+ **Pull-request states.** Run
259
+ `gh pr view 'https://github.com/<owner>/<repo>/pull/<n>' --json state -q .state` against the URL
260
+ `External links:` lists:
261
+
262
+ - `OPEN`: work in flight. Skip it; the issue stays where it is.
263
+ - `MERGED`: the change landed. Take nothing and set nothing: an issue Legion ran is already skipped
264
+ by the `Legion ran it without the label now on it` row, and the daemon moves such an issue to its
265
+ production check itself on the merge event; one Legion did not run is someone's finished work with
266
+ the issue left open. Skip it and name it in your summary
267
+ (`<KEY>: pull request <url> merged, the issue is still todo`) so a person closes it.
268
+ - `CLOSED` (not merged): someone abandoned the change. Skip it and name it in your summary; the
269
+ issue's owner decides what follows. For an issue Legion runs, the daemon tells its architect
270
+ (`pr-closed-unmerged`); a walk candidate has no architect, so the summary is where it is reported.
271
+ - The command fails (the token file missing, refused, or the repository unreadable): report the gap
272
+ in your summary with `gh`'s first stderr line (`<KEY>: pull request <url> unreadable: …`), skip
273
+ the issue, and never read the failure as any of the three states. Text that is not the shape
274
+ `https://github.com/<owner>/<repo>/pull/<n>` is not a pull-request URL and is never run as a
275
+ command argument: skip the issue and name it in your summary
276
+ (`<KEY>: pull request <text> unreadable: not a pull-request URL`).
277
+
278
+ A person who wants Legion on it anyway hands it over themselves: the label, then `todo`.
279
+
248
280
  **Take.** For each candidate that passes, in order:
249
281
 
250
282
  1. Add the label and keep the labels it has, which `Labels:` lists (`none` is no labels). The
251
- `--label` flags replace the whole set, so a label you leave out is removed.
283
+ `--label` flags replace the whole set, so a label you leave out is removed. Pass each label
284
+ single-quoted, exactly as `Labels:` shows it; a label holding a `'` or a line break cannot be
285
+ single-quoted and is never put in a command: skip the issue and name it in your summary
286
+ (`<KEY>: label <label> cannot be quoted`) so a person hands it over themselves.
252
287
 
253
288
  ```text
254
- dispatch issue-update --issue <KEY> --label <each current label> --label legion
289
+ dispatch issue-update --issue <KEY> --label '<each current label>' --label legion
255
290
  ```
256
291
 
257
292
  2. It is already in `todo`, so the label admits it: the daemon records it and gives it the free
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion",
3
- "version": "8.13.0",
3
+ "version": "8.14.1",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "omp": {
@@ -12,7 +12,7 @@
12
12
  ]
13
13
  },
14
14
  "legion": {
15
- "daemonApiVersion": 19
15
+ "daemonApiVersion": 20
16
16
  },
17
17
  "repository": {
18
18
  "type": "git",