@sjawhar/pi-legion-envoy 1.24.1 → 1.24.3

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
@@ -30852,7 +30852,7 @@ function createLegionTool(deps) {
30852
30852
  return {
30853
30853
  name: "legion",
30854
30854
  label: "legion",
30855
- description: "Perform a Legion lifecycle write through the Legion daemon. " + "register_gate records the root spec document a human must approve: `artifactId` is the " + "document id (a UUID) and `version` the version number, both copied from the `artifact` and " + "`version` fields of dispatch_request_approval's result \u2014 never the slug or file name you " + "passed to that tool. " + `spawn_worker's response "status" means: "spawned" \u2014 a fresh pane just opened and is ` + 'running now; "resumed" \u2014 an existing worker was prompted directly over its live socket, ' + "or (if its boot has not confirmed yet) its task was recorded to deliver once that boot " + 'completes; "queued" \u2014 the running-worker cap is full, the task was recorded and this ' + 'role will start on its own once a slot frees. Never re-spawn a role after "resumed" or ' + '"queued" \u2014 wait for the worker-started notification instead.',
30855
+ description: "Perform a Legion lifecycle write through the Legion daemon. " + "register_gate records the root spec document a human must approve: `artifactId` is the " + "document id (a UUID) and `version` the version number, both copied from the `artifact` and " + "`version` fields of dispatch_request_approval's result \u2014 never the slug or file name you " + "passed to that tool. " + `spawn_worker's response "status" means: "spawned" \u2014 a fresh pane just opened and is ` + 'running now; "resumed" \u2014 an existing worker was prompted directly over its live socket ' + "and its turn started, or (if its boot has not confirmed yet) its task was recorded to " + 'deliver once that boot completes; "queued" \u2014 the task was recorded and this role will ' + "start on its own: either the running-worker cap is full, or the live worker acknowledged " + "the task without starting a turn and the daemon is retrying it. Never re-spawn a role " + 'after "resumed" or "queued" \u2014 wait for the worker-started notification instead.',
30856
30856
  defaultInactive: true,
30857
30857
  parameters: legionToolSchema(pi),
30858
30858
  execute: async (_id, parameters, _signal, _onUpdate, context) => {
@@ -30961,25 +30961,6 @@ function createLegionTool(deps) {
30961
30961
  };
30962
30962
  }
30963
30963
 
30964
- // src/legion/workspace-helpers.ts
30965
- async function setJjIdentity(cwd, gitName, gitEmail) {
30966
- for (const [key, value] of [
30967
- ["user.name", gitName],
30968
- ["user.email", gitEmail]
30969
- ]) {
30970
- const child = Bun.spawn(["jj", "config", "set", "--repo", key, JSON.stringify(value)], {
30971
- cwd,
30972
- stdout: "ignore",
30973
- stderr: "pipe"
30974
- });
30975
- const exitCode = await child.exited;
30976
- if (exitCode === 0)
30977
- continue;
30978
- const stderr = await new Response(child.stderr).text();
30979
- throw new Error(`jj config set ${key} failed: ${stderr.trim()}`);
30980
- }
30981
- }
30982
-
30983
30964
  // extensions/envoy.ts
30984
30965
  import { existsSync } from "fs";
30985
30966
  import { dirname, resolve } from "path";
@@ -35047,7 +35028,6 @@ function legionExtension(pi) {
35047
35028
  if (bootstrap)
35048
35029
  return bootstrap;
35049
35030
  const bootToken = requiredSecret(process.env, "LEGION_BOOT_TOKEN");
35050
- const workspace = requiredEnvironment(process.env, "LEGION_WORKSPACE");
35051
35031
  bootstrap = (async () => {
35052
35032
  const { sessionFile, agentId } = await persistedTranscript(context);
35053
35033
  const started = await (async () => {
@@ -35071,7 +35051,6 @@ function legionExtension(pi) {
35071
35051
  })();
35072
35052
  try {
35073
35053
  await exportJjSessionAttribution(sessionFile, requiredEnvironment(process.env, "LEGION_STATE_DIR"));
35074
- await setJjIdentity(workspace, started.gitName, started.gitEmail);
35075
35054
  capability = {
35076
35055
  kind: "phase-worker",
35077
35056
  sessionID,
@@ -163,6 +163,12 @@ in flight. On each child closure, re-scope open work, close obsolete work with a
163
163
  release the next wave only when it now makes sense. There is no inter-child dependency
164
164
  mechanism to encode.
165
165
 
166
+ Release admits nothing. A child never takes an admission slot or becomes a root tree of its
167
+ own: the daemon ignores a child's `todo` while your tree is live, and this `spawn_worker` is
168
+ what starts the child — the daemon writes its Dispatch status `in_progress` on the first
169
+ sub-architect spawn while the child is at `todo`. A released child with no sub-architect stays
170
+ at `todo` until you spawn one.
171
+
166
172
  ## 3. Children complete
167
173
 
168
174
  Treat `children-complete` as the edge into the end-game, not as a reason to close the
@@ -276,13 +282,15 @@ corresponding lifecycle procedure.
276
282
 
277
283
  | Wake | Procedure |
278
284
  | --- | --- |
285
+ | `child-adopted` | Payload `{type:"child-adopted", child, remaining}`. A child is now in your tree — one created under this issue (by you or a human), or one a daemon upgrade moved back into your tree from a root tree of its own (LEGION-57). If Dispatch shows it released **and open** — `todo` through `retro`, never `done`; `remaining` counts exactly those — and `legion state` shows no `roles` entry with `issue` = the child and `role: "architect"`, `spawn_worker` its architect now. An unreleased child waits for its wave; a `done` child is finished and gets nothing, whatever stray tree of its own `legion state` may still show. |
286
+ | `child-status` | Payload `{type:"child-status", child, from, to}`. Your child's Dispatch status changed. `to: "todo"` with no architect claim for the child (`legion state`) means it is released and unowned — your own `release_wave` echo, or a human's move — so `spawn_worker` its architect. `to: "backlog"` or `"icebox"` means the child was de-prioritised (a human's move, or your own `set_status`): a child has no tree of its own, so the daemon stops nothing on that move — tell its sub-architect (`envoy_publish` to its role topic) to finish the step in flight and park, or re-scope it; its finished workers idle-retire, and it resumes from its session on your next `spawn_worker` once the child is released again. Any other transition is information for re-scoping. |
279
287
  | `child-closed` | Read the child completion and remaining open children. Re-scope or close obsolete open work; release an appropriate next wave, or await `children-complete`. |
280
288
  | `children-complete` | Execute steps 3–4: parent integration verification; failures become a new child wave, success advances to review and retro. |
281
289
  | `child-reopened` | Treat the completion edge as reset. Reassess the reopened child and return the tree to children-in-flight; do not continue an already-started end-game. |
282
290
  | `design-approved` | Payload `{type:"design-approved"}`. A human approved the root spec document at its current version; the gate is open. Proceed to section 2. |
283
291
  | `design-changes-requested` | Payload `{type:"design-changes-requested", version, reason, author?}`. A human asked for changes to the root spec at `version`, for `reason`. Revise the spec, call `dispatch_request_approval` again, and stay parked; the gate is closed. |
284
292
  | `phase-complete` | Payload `{type:"phase-complete", issue, role, summary}`. May arrive live or via `catchup-overseer`'s `phaseCompletions`. Read the committed handoff for that phase, then spawn the next phase's owner, or `spawn_worker` on the same role again to resume it with corrections if the handoff shows unresolved gaps. A `reviewer` completion whose GitHub review is `CHANGES_REQUESTED` (the daemon returns the issue's Dispatch status to `in_progress` for this, on the reviewer's completion and again when you spawn the corrective implementer unless the daemon already knows the issue is `in_progress`) means `spawn_worker` the **implementer** again with the review findings — thread URLs and blocking items — as its task, then route back through tester and reviewer in order; never `spawn_worker` the reviewer directly off this wake and never proceed to retro on this verdict. A reviewer completion with an `APPROVED` review proceeds to retro (step 5). A `reviewer` completion after a conflict-forced rebase whose review body names an unchanged fingerprint is a confirmation, not a round: if retro already completed, `spawn_worker` the merger; otherwise resume the step you were on. |
285
- | `worker-queued` | Payload `{type:"worker-queued", issue, role}`. The deployment's worker cap is full; this role's spawn is queued. Do not respawn or retry — wait for `worker-started`. |
293
+ | `worker-queued` | Payload `{type:"worker-queued", issue, role}`. This role's task is queued for promotion — either the deployment's worker cap is full, or the live worker acknowledged the task without starting a turn and the daemon is retrying it (counted; the worker is replaced after three such failures, still with the same task). Do not respawn or retry — wait for `worker-started`. |
286
294
  | `worker-started` | Payload `{type:"worker-started", issue, role}`. A previously queued role has been promoted and is now running. Treat it exactly as a normal spawn: resume tracking that role's live session. |
287
295
  | `pr-ready` | Verify the live PR head, green status, and review state. Continue the review/retro/merger order only for that current head. |
288
296
  | `pr-review` | Payload `{type:"pr-review", state, author, body}`. Delivered to whichever role is currently active for the issue, falling back to you when no worker phase is active. Follows the same verdict rule as a reviewer's `phase-complete`: `state: "changes_requested"` sends the implementer back in with the review findings, then tester, then reviewer — never the reviewer again and never retro; that `spawn_worker` returns the issue to `in_progress` on its own (the daemon writes it for a corrective implementer whenever the PR's latest recorded review is changes requested, a human's after approval included), so you set nothing by hand; `state: "approved"` proceeds toward retro (step 5) once the step 6 integration/merge-gate conditions are met. `state: "approved"` on a rebased head whose body names an unchanged fingerprint is that confirmation: proceed to retro if it has not run, otherwise to the merger — never to a second retro or test round. |
@@ -290,7 +298,7 @@ corresponding lifecycle procedure.
290
298
  | `pr-merged` | Payload `{type:"pr-merged", pr, mergeCommitSha}`. The merge queue landed the PR. This is your cue for step 7: post the sign-off comment naming that merge commit and set the issue `done`. Nothing else follows a merge. |
291
299
  | `pr-closed-unmerged` | Decide from current scope whether to reopen the work, send a fresh implementer, or cancel it with a reason. Delegate the repository action to the responsible phase worker and keep ownership. |
292
300
  | `issue-comment` | Interpret the comment in the issue's design context. Answer it, adjust the plan, or relay it via `envoy_publish` to the responsible worker's role token; scope and product decisions remain with you. |
293
- | `catchup-overseer` | Verify its gates, child counts, and PR verdicts against current artifacts, then resume the applicable numbered lifecycle step. It is a current-state snapshot, not a raw-event replay. `gates[LEGION_TREE].open` is the design gate's current state: `true` means the root spec is approved at its current version and you may spawn; `false` (or no `open` key, meaning no gate is registered) means the sequence in section 1 still applies. For each entry in its `phaseCompletions` (`{issue, role, summary, at}`, phases that completed while you were not live), handle it exactly as a `phase-complete` wake. |
301
+ | `catchup-overseer` | Verify its gates, child counts, and PR verdicts against current artifacts, then resume the applicable numbered lifecycle step. It is a current-state snapshot, not a raw-event replay. `gates[LEGION_TREE].open` is the design gate's current state: `true` means the root spec is approved at its current version and you may spawn; `false` (or no `open` key, meaning no gate is registered) means the sequence in section 1 still applies. For each entry in its `phaseCompletions` (`{issue, role, summary, at}`, phases that completed while you were not live), handle it exactly as a `phase-complete` wake. Then compare `childCounts[LEGION_ISSUE].open` (the children not `done`) with `legion state` and Dispatch: any **open** released child — `todo` through `retro` — with no architect role claim gets `spawn_worker` for its architect, a `child-adopted` or `child-status` wake you missed while not live; a `done` child gets nothing, whether or not a lingering legacy tree of its own still shows in `legion state`. |
294
302
  | `worker-died` | Payload `{type:"worker-died", issue, role}`. The daemon probed and retried this role's worker through `MAX_LAUNCH_FAILURES` attempts and could not confirm a boot — never a raw-event replay or a silent revive. Reassess the work and `spawn_worker` again for the role (it resumes the same agent via `--resume` if a session file survived) or reassign it if the failure looks environmental, not agent-specific. |
295
303
  | `reopened` | Reopen the root lifecycle: inspect the reason and current artifacts, reassess scope and children, and resume at the first applicable numbered step. |
296
304
 
@@ -149,9 +149,22 @@ Commit attribution is automatic: the extension exports a `JJ_CONFIG` overlay whe
149
149
  session starts, so every jj commit you make carries an `Omp-Session: <this-session-id>`
150
150
  trailer with no action from you. Do not add attribution trailers by hand.
151
151
 
152
- The jj configuration already supplies your phase's plus-addressed author and committer
153
- identity. Do not override Git identity configuration. Your session receives the credential
154
- capability it needs; invoke GitHub through the credential helper:
152
+ Your pane's environment already supplies your phase's author and committer identity
153
+ (`JJ_USER`/`JJ_EMAIL` and the Git author/committer variables, set by the daemon when it opened
154
+ the pane; the daemon also re-authors the workspace's working copy for your role at each
155
+ assignment, since `jj split`/`jj describe` keep its author). Never set or override
156
+ `user.name`/`user.email` in any jj or Git scope — not `jj config set`, not `--config`, not
157
+ `git config`: `--config` outranks the pane environment and would put the wrong App back on your
158
+ commits, and the repository-scoped jj config is one file shared by every issue workspace of the
159
+ clone. Before a push, check
160
+ `jj -R "$LEGION_WORKSPACE" log -r 'main@origin..@' -T 'author.email() ++ " | " ++ committer.email() ++ " " ++ description.first_line() ++ "\n"'`
161
+ shows your role's App in both columns **on every commit you made** — not on the whole list:
162
+ earlier phases' commits are legitimately authored by their own role's App, and a conflict-forced
163
+ rebase legitimately sets the committer of every rebased commit, other roles' included, to the
164
+ rebaser. A wrong identity on your own commit is a pane-environment problem to report to the
165
+ architect, not something to pin (`docs/solutions/legion/shared-main-repo-hazards-for-concurrent-issue-workspaces.md`,
166
+ Hazard 1). Your session receives the credential capability it needs; invoke GitHub through the
167
+ credential helper:
155
168
 
156
169
  ```bash
157
170
  legion gh -- <gh args…>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "1.24.1",
3
+ "version": "1.24.3",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [