@sjawhar/pi-legion-envoy 1.22.2 → 1.23.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/envoy.js
CHANGED
|
@@ -30279,8 +30279,9 @@ var stateTree = strictObject({
|
|
|
30279
30279
|
locator: stateTreeLocator.optional()
|
|
30280
30280
|
});
|
|
30281
30281
|
var stateGate = strictObject({
|
|
30282
|
-
|
|
30283
|
-
|
|
30282
|
+
artifactId: nonEmptyString,
|
|
30283
|
+
latestVersion: number2().int().positive(),
|
|
30284
|
+
approvedVersion: number2().int().positive().optional()
|
|
30284
30285
|
});
|
|
30285
30286
|
var stateRole = strictObject({
|
|
30286
30287
|
role: nonEmptyString,
|
|
@@ -30422,7 +30423,11 @@ var LegionDaemonApi = {
|
|
|
30422
30423
|
response: object({})
|
|
30423
30424
|
},
|
|
30424
30425
|
GatesRegister: {
|
|
30425
|
-
request: architectCapability.extend({
|
|
30426
|
+
request: architectCapability.extend({
|
|
30427
|
+
issue: nonEmptyString,
|
|
30428
|
+
artifactId: uuid2(),
|
|
30429
|
+
version: number2().int().positive()
|
|
30430
|
+
}),
|
|
30426
30431
|
response: object({})
|
|
30427
30432
|
},
|
|
30428
30433
|
Grant: {
|
|
@@ -32629,7 +32634,7 @@ ${trailer.join(`
|
|
|
32629
32634
|
const result = await client.requestApproval(resolved.artifact.id, { actor });
|
|
32630
32635
|
if (result.ask === null) {
|
|
32631
32636
|
return {
|
|
32632
|
-
text: `${resolved.artifact.name} is already approved at version ${result.version} by ${result.approval.by?.id ?? "unknown"}; no new request was opened. An edit after approval makes it stale, so request again only for a new version.`,
|
|
32637
|
+
text: `${resolved.artifact.name} (document id ${resolved.artifact.id}) is already approved at version ${result.version} by ${result.approval.by?.id ?? "unknown"}; no new request was opened. An edit after approval makes it stale, so request again only for a new version.`,
|
|
32633
32638
|
details: {
|
|
32634
32639
|
...resolved.owner.kind === "project" ? documentResultDetails(resolved.artifact) : { issue: resolved.issue?.key },
|
|
32635
32640
|
artifact: resolved.artifact.id,
|
|
@@ -32639,7 +32644,7 @@ ${trailer.join(`
|
|
|
32639
32644
|
}
|
|
32640
32645
|
const details = await askResultDetails(client, result.ask, resolved);
|
|
32641
32646
|
return {
|
|
32642
|
-
text: `Approval requested for ${resolved.artifact.name} at version ${result.version} (ask ${result.ask.id}). The answer arrives as artifact.approved or artifact.changes_requested; an edit after approval makes it stale, so request again for the new version.`,
|
|
32647
|
+
text: `Approval requested for ${resolved.artifact.name} (document id ${resolved.artifact.id}) at version ${result.version} (ask ${result.ask.id}). The answer arrives as artifact.approved or artifact.changes_requested; an edit after approval makes it stale, so request again for the new version.`,
|
|
32643
32648
|
details: { ...details, artifact: resolved.artifact.id, version: result.version }
|
|
32644
32649
|
};
|
|
32645
32650
|
}
|
package/dist/legion.js
CHANGED
|
@@ -30279,8 +30279,9 @@ var stateTree = strictObject({
|
|
|
30279
30279
|
locator: stateTreeLocator.optional()
|
|
30280
30280
|
});
|
|
30281
30281
|
var stateGate = strictObject({
|
|
30282
|
-
|
|
30283
|
-
|
|
30282
|
+
artifactId: nonEmptyString,
|
|
30283
|
+
latestVersion: number2().int().positive(),
|
|
30284
|
+
approvedVersion: number2().int().positive().optional()
|
|
30284
30285
|
});
|
|
30285
30286
|
var stateRole = strictObject({
|
|
30286
30287
|
role: nonEmptyString,
|
|
@@ -30422,7 +30423,11 @@ var LegionDaemonApi = {
|
|
|
30422
30423
|
response: object({})
|
|
30423
30424
|
},
|
|
30424
30425
|
GatesRegister: {
|
|
30425
|
-
request: architectCapability.extend({
|
|
30426
|
+
request: architectCapability.extend({
|
|
30427
|
+
issue: nonEmptyString,
|
|
30428
|
+
artifactId: uuid2(),
|
|
30429
|
+
version: number2().int().positive()
|
|
30430
|
+
}),
|
|
30426
30431
|
response: object({})
|
|
30427
30432
|
},
|
|
30428
30433
|
Grant: {
|
|
@@ -30796,9 +30801,12 @@ var LIFECYCLE_STATUSES2 = [
|
|
|
30796
30801
|
function isLifecycleStatus(value) {
|
|
30797
30802
|
return LIFECYCLE_STATUSES2.includes(value);
|
|
30798
30803
|
}
|
|
30804
|
+
function isDispatchArtifactId(value) {
|
|
30805
|
+
return LegionDaemonApi.GatesRegister.request.shape.artifactId.safeParse(value).success;
|
|
30806
|
+
}
|
|
30799
30807
|
var LEGION_OP_FIELDS = {
|
|
30800
30808
|
set_status: ["issue", "status"],
|
|
30801
|
-
register_gate: ["issue", "
|
|
30809
|
+
register_gate: ["issue", "artifactId", "version"],
|
|
30802
30810
|
release_wave: ["issues"],
|
|
30803
30811
|
escalate: ["kind", "context"],
|
|
30804
30812
|
spawn_worker: ["issue", "role", "task"]
|
|
@@ -30809,7 +30817,8 @@ function legionToolSchema(pi) {
|
|
|
30809
30817
|
op: z.enum(["set_status", "register_gate", "release_wave", "escalate", "spawn_worker"]),
|
|
30810
30818
|
issue: z.string().optional(),
|
|
30811
30819
|
status: z.enum(LIFECYCLE_STATUSES2).optional(),
|
|
30812
|
-
|
|
30820
|
+
artifactId: z.string().optional(),
|
|
30821
|
+
version: z.number().optional(),
|
|
30813
30822
|
kind: z.enum(["re-file", "capacity", "cross-tree"]).optional(),
|
|
30814
30823
|
context: z.unknown().optional(),
|
|
30815
30824
|
issues: z.array(z.string()).optional(),
|
|
@@ -30823,7 +30832,7 @@ function createLegionTool(deps) {
|
|
|
30823
30832
|
return {
|
|
30824
30833
|
name: "legion",
|
|
30825
30834
|
label: "legion",
|
|
30826
|
-
description: "Perform a Legion lifecycle write through the Legion daemon. " + `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.',
|
|
30835
|
+
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.',
|
|
30827
30836
|
defaultInactive: true,
|
|
30828
30837
|
parameters: legionToolSchema(pi),
|
|
30829
30838
|
execute: async (_id, parameters, _signal, _onUpdate, context) => {
|
|
@@ -30861,15 +30870,25 @@ function createLegionTool(deps) {
|
|
|
30861
30870
|
});
|
|
30862
30871
|
return jsonSuccess({});
|
|
30863
30872
|
}
|
|
30864
|
-
case "register_gate":
|
|
30873
|
+
case "register_gate": {
|
|
30874
|
+
const version = parameters.version;
|
|
30875
|
+
if (typeof version !== "number" || !Number.isSafeInteger(version) || version <= 0) {
|
|
30876
|
+
throw new Error("register_gate requires a positive integer version");
|
|
30877
|
+
}
|
|
30878
|
+
const artifactId = stringInput("artifactId");
|
|
30879
|
+
if (!isDispatchArtifactId(artifactId)) {
|
|
30880
|
+
throw new Error(`register_gate requires artifactId to be the document id (a UUID) from dispatch_request_approval's result, not "${artifactId}"`);
|
|
30881
|
+
}
|
|
30865
30882
|
await daemon.gatesRegister({
|
|
30866
30883
|
tree: architect.tree,
|
|
30867
30884
|
sessionId,
|
|
30868
30885
|
secret: architect.secret,
|
|
30869
30886
|
issue: stringInput("issue"),
|
|
30870
|
-
|
|
30887
|
+
artifactId,
|
|
30888
|
+
version
|
|
30871
30889
|
});
|
|
30872
30890
|
return jsonSuccess({});
|
|
30891
|
+
}
|
|
30873
30892
|
case "release_wave": {
|
|
30874
30893
|
const issues = parameters.issues;
|
|
30875
30894
|
if (!Array.isArray(issues) || !issues.every((issue) => typeof issue === "string")) {
|
|
@@ -33045,7 +33064,7 @@ ${trailer.join(`
|
|
|
33045
33064
|
const result = await client.requestApproval(resolved.artifact.id, { actor });
|
|
33046
33065
|
if (result.ask === null) {
|
|
33047
33066
|
return {
|
|
33048
|
-
text: `${resolved.artifact.name} is already approved at version ${result.version} by ${result.approval.by?.id ?? "unknown"}; no new request was opened. An edit after approval makes it stale, so request again only for a new version.`,
|
|
33067
|
+
text: `${resolved.artifact.name} (document id ${resolved.artifact.id}) is already approved at version ${result.version} by ${result.approval.by?.id ?? "unknown"}; no new request was opened. An edit after approval makes it stale, so request again only for a new version.`,
|
|
33049
33068
|
details: {
|
|
33050
33069
|
...resolved.owner.kind === "project" ? documentResultDetails(resolved.artifact) : { issue: resolved.issue?.key },
|
|
33051
33070
|
artifact: resolved.artifact.id,
|
|
@@ -33055,7 +33074,7 @@ ${trailer.join(`
|
|
|
33055
33074
|
}
|
|
33056
33075
|
const details = await askResultDetails(client, result.ask, resolved);
|
|
33057
33076
|
return {
|
|
33058
|
-
text: `Approval requested for ${resolved.artifact.name} at version ${result.version} (ask ${result.ask.id}). The answer arrives as artifact.approved or artifact.changes_requested; an edit after approval makes it stale, so request again for the new version.`,
|
|
33077
|
+
text: `Approval requested for ${resolved.artifact.name} (document id ${resolved.artifact.id}) at version ${result.version} (ask ${result.ask.id}). The answer arrives as artifact.approved or artifact.changes_requested; an edit after approval makes it stale, so request again for the new version.`,
|
|
33059
33078
|
details: { ...details, artifact: resolved.artifact.id, version: result.version }
|
|
33060
33079
|
};
|
|
33061
33080
|
}
|
|
@@ -33,7 +33,8 @@ separate coordinator to finish necessary work.
|
|
|
33
33
|
hold (your own, or one `spawn_worker` returned) or compute another with the
|
|
34
34
|
`roleToken` helper from `@legion/contracts` exactly the way the daemon does.
|
|
35
35
|
- There is no label vocabulary. Dispatch status replaces the board, and the design gate
|
|
36
|
-
is a
|
|
36
|
+
is a human approving the root spec document at a version in Dispatch, requested with
|
|
37
|
+
`dispatch_request_approval` — not a label and not an ask. Never attempt to apply a label.
|
|
37
38
|
- Deferring necessary work is failure. The sole valid deferral is a new child issue you
|
|
38
39
|
create and continue to own. Re-file a genuinely independent child through the
|
|
39
40
|
controller rather than treating it as an abandoned dependency.
|
|
@@ -87,34 +88,51 @@ adoption/decomposition and waves, acceptance criteria, and the integration test
|
|
|
87
88
|
"spec" artifact beside it (`dispatch_artifact` with the primary document's name replaces the
|
|
88
89
|
human's document; do not do that). Both readers described in
|
|
89
90
|
[Writing for the human](../dispatch/SKILL.md#writing-for-the-human) must be able to follow it.
|
|
90
|
-
|
|
91
|
-
|
|
91
|
+
The design gate runs only when the "Design gate policy" line at the end of your system prompt
|
|
92
|
+
says `gates.design: root-issues`. When it says `gates.design: off`, write the spec and continue
|
|
93
|
+
to section 2 with no approval step at all: do not request approval, do not register a gate, and
|
|
94
|
+
do not wait for `design-approved`. A sub-architect on a child issue has no policy line and never
|
|
95
|
+
runs the gate either: the root approval covers the tree. When the gate is armed, run this exact
|
|
96
|
+
sequence **before any Legion-role spawn**, including a sub-architect:
|
|
92
97
|
|
|
93
98
|
```text
|
|
94
99
|
dispatch_doc_edit({ issue: "<root issue>", ... }) // extend the primary document in place
|
|
95
|
-
|
|
100
|
+
result = dispatch_request_approval({ issue: "<root issue>" }) // the primary document by default
|
|
101
|
+
legion({
|
|
102
|
+
op: "register_gate",
|
|
96
103
|
issue: "<root issue>",
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
{ label: "Approve", description: "Work starts as described; the first worker is spawned now." },
|
|
100
|
-
{ label: "Hold", description: "Nothing starts; reply on the issue with what should change first." },
|
|
101
|
-
]
|
|
104
|
+
artifactId: result.details.artifact, // the document id, a UUID such as 4e0aca36-77b3-43bd-96cf-d58890ae64e4
|
|
105
|
+
version: result.details.version, // the version number the human is asked to approve
|
|
102
106
|
})
|
|
103
|
-
legion({ op: "register_gate", issue: "<root issue>", askId })
|
|
104
107
|
```
|
|
105
108
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
109
|
+
`dispatch_request_approval` opens a system question on the document with the fixed options
|
|
110
|
+
`Approve` and `Request changes`; a human answers it from the Inbox or approves from the
|
|
111
|
+
document's own header. Never open a `dispatch_ask` with an `Approve` option yourself: an
|
|
112
|
+
ordinary question is not a gate and the daemon ignores its answer. Copy `artifactId` and
|
|
113
|
+
`version` from the result of `dispatch_request_approval` — its text reads "Approval requested for
|
|
114
|
+
spec.md (document id <UUID>) at version <N>" and its `details.artifact` / `details.version` carry
|
|
115
|
+
the same two values. The document id is never the slug or file name you passed in (`spec`,
|
|
116
|
+
`spec.md`): the daemon recognizes the document's approval events by that id, and both the
|
|
117
|
+
`legion` tool and the daemon refuse a value that is not a UUID. Calling
|
|
118
|
+
`dispatch_request_approval` again while a request is open returns the same open request, so it
|
|
119
|
+
is safe to repeat. If its text instead reads "spec.md (document id <UUID>) is already approved at
|
|
120
|
+
version <N>" — a human approved from the document header before you asked — still call
|
|
121
|
+
`register_gate` with that id and version: the daemon reads the approval from Dispatch as it
|
|
122
|
+
registers, opens the gate, and delivers `design-approved` at once. The same read covers a human
|
|
123
|
+
who answers the question between your `dispatch_request_approval` and `register_gate` calls, so
|
|
124
|
+
an approval is never lost to timing; you never approve anything yourself.
|
|
125
|
+
|
|
126
|
+
Then park. Do not release a wave or spawn a Legion role until a later delivered wake shows
|
|
127
|
+
`design-approved` on the root. On `design-changes-requested`, revise the spec (a new version of
|
|
128
|
+
the primary document), call `dispatch_request_approval` again — it re-opens the request at the
|
|
129
|
+
new version — and stay parked. Approval is pinned to the spec version: editing the root spec
|
|
130
|
+
after approval closes the gate again with no wake (you made the edit, or the `artifact.version`
|
|
131
|
+
event on your issue tells you), so call `dispatch_request_approval` again, and release no new
|
|
132
|
+
wave and spawn no new role until the next `design-approved` arrives — work already in flight
|
|
133
|
+
continues. Later waves, re-scopes, and integration-failure children that leave the root spec
|
|
134
|
+
untouched need no new approval, and a child issue's spec is never gated: the root approval covers
|
|
135
|
+
the tree.
|
|
118
136
|
|
|
119
137
|
## 2. Children in flight
|
|
120
138
|
|
|
@@ -186,13 +204,13 @@ legion({
|
|
|
186
204
|
op: "spawn_worker",
|
|
187
205
|
issue: "LEGION-40",
|
|
188
206
|
role: "implementer",
|
|
189
|
-
task: "Run the legion-retro skill now. Capture durable learnings and post the issue
|
|
207
|
+
task: "Run the legion-retro skill now. Capture durable learnings and post the retro message on the Dispatch issue with dispatch_message; do not create a .legion handoff file."
|
|
190
208
|
})
|
|
191
209
|
```
|
|
192
210
|
|
|
193
211
|
Wait for the implementer to report its durable retro result. Retro output is
|
|
194
|
-
`docs/solutions/` plus
|
|
195
|
-
the reviewer-approved head after cleanup.
|
|
212
|
+
`docs/solutions/` plus one `dispatch_message` on the issue; it must not create a `.legion`
|
|
213
|
+
file or rewrite the reviewer-approved head after cleanup.
|
|
196
214
|
|
|
197
215
|
## 6. Architect sign-off and merge
|
|
198
216
|
|
|
@@ -261,6 +279,8 @@ corresponding lifecycle procedure.
|
|
|
261
279
|
| `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`. |
|
|
262
280
|
| `children-complete` | Execute steps 3–4: parent integration verification; failures become a new child wave, success advances to review and retro. |
|
|
263
281
|
| `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
|
+
| `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
|
+
| `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. |
|
|
264
284
|
| `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. |
|
|
265
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`. |
|
|
266
286
|
| `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. |
|
|
@@ -270,7 +290,7 @@ corresponding lifecycle procedure.
|
|
|
270
290
|
| `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. |
|
|
271
291
|
| `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. |
|
|
272
292
|
| `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. |
|
|
273
|
-
| `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. 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. |
|
|
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. |
|
|
274
294
|
| `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. |
|
|
275
295
|
| `reopened` | Reopen the root lifecycle: inspect the reason and current artifacts, reassess scope and children, and resume at the first applicable numbered step. |
|
|
276
296
|
|
|
@@ -129,7 +129,11 @@ on stale entries until their source artifact explains the anomaly.
|
|
|
129
129
|
## Mentions
|
|
130
130
|
|
|
131
131
|
Read the mention and its artifact. Answer it when it asks the controller for triage or
|
|
132
|
-
human-facing information.
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
human-facing information. A human asking how to let a root proceed past its design gate
|
|
133
|
+
approves the root issue's spec document in Dispatch — the `Approve` control in the document's
|
|
134
|
+
header, or the approval question the architect's request opened in the Inbox. The controller
|
|
135
|
+
never opens a gate and there is no operator command for it; a project that does not want the
|
|
136
|
+
gate at all runs `gates.design: off` in its `legion.yaml`. Otherwise resolve the authoritative
|
|
137
|
+
owning architect role and route the verified context with `envoy_publish`. Do not route raw
|
|
138
|
+
event traffic or invent a role token from a partial issue reference.
|
|
135
139
|
|
|
@@ -17,7 +17,8 @@ retrospective's durable output.
|
|
|
17
17
|
1. Tester green and all code-review cycles finish.
|
|
18
18
|
2. The implementer pushes the `.legion/` deletion at the reviewer's direction, and the reviewer
|
|
19
19
|
approves that head.
|
|
20
|
-
3. Run this retro: commit durable learnings to `docs/solutions/` and post the
|
|
20
|
+
3. Run this retro: commit durable learnings to `docs/solutions/` and post the retro message on
|
|
21
|
+
the Dispatch issue.
|
|
21
22
|
Retro writes **no `.legion` file**, so it never re-dirties the cleaned handoff tree.
|
|
22
23
|
4. The merger verifies the tip is the approved head plus commits that change only
|
|
23
24
|
`docs/solutions/` — `jj diff --from <approved-sha> --to <tip-sha> --summary`, quoted in READY —
|
|
@@ -66,18 +67,22 @@ date: YYYY-MM-DD
|
|
|
66
67
|
status: active
|
|
67
68
|
module: affected-module
|
|
68
69
|
related_issues:
|
|
69
|
-
- "
|
|
70
|
+
- "LEGION-123" # the Dispatch issue
|
|
71
|
+
- "owner/repo#456" # the pull request
|
|
70
72
|
---
|
|
71
73
|
```
|
|
72
74
|
|
|
73
75
|
Commit the documentation on the existing issue branch, advance its existing bookmark, and push
|
|
74
|
-
that branch. Do not create a replacement branch or bookmark. Then post
|
|
75
|
-
the
|
|
76
|
-
|
|
76
|
+
that branch. Do not create a replacement branch or bookmark. Then post one Dispatch message on
|
|
77
|
+
the issue — `issue` is your `LEGION_ISSUE`; Legion issues live on Dispatch, never on a GitHub
|
|
78
|
+
issue, and the `gh` shim refuses every GitHub-issue write — naming the documents and the
|
|
79
|
+
one-to-three most useful takeaways. The message must carry this revived implementer's structured
|
|
80
|
+
attribution footer with `phase` set to `retro`; the body is capped at 2,000 characters:
|
|
77
81
|
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
|
|
82
|
+
```ts
|
|
83
|
+
dispatch_message({
|
|
84
|
+
issue: "<KEY>",
|
|
85
|
+
body: `## Retro Complete
|
|
81
86
|
|
|
82
87
|
**Learnings documented in:**
|
|
83
88
|
- docs/solutions/<path>.md
|
|
@@ -85,11 +90,11 @@ legion gh -- issue comment <issue-number> \
|
|
|
85
90
|
**Key takeaways:**
|
|
86
91
|
- <reusable lesson>
|
|
87
92
|
|
|
88
|
-
<!-- legion: {"session":"<session-id>","phase":"retro"}
|
|
89
|
-
|
|
93
|
+
<!-- legion: {"session":"<session-id>","phase":"retro"} -->`,
|
|
94
|
+
})
|
|
90
95
|
```
|
|
91
96
|
|
|
92
|
-
The
|
|
97
|
+
The Dispatch message and the `docs/solutions/` commit are the only retro outputs. Never write a
|
|
93
98
|
handoff, phase artifact, local feedback log, or completion label; `.legion/` was deleted before
|
|
94
99
|
retro and nothing recreates it. Report completion with `legion handoff complete` alone (its
|
|
95
100
|
summary: two sentences for the architect) — no `legion handoff write`.
|
|
@@ -100,7 +105,7 @@ Before returning, verify all of the following:
|
|
|
100
105
|
|
|
101
106
|
- The reviewer cleanup commit remains below the retro documentation commit, and the reviewer's
|
|
102
107
|
approval of that cleanup head stands: the merger accepts the approved head plus this commit.
|
|
103
|
-
- The learning documents and
|
|
108
|
+
- The learning documents and the Dispatch message both exist.
|
|
104
109
|
- No `.legion` file was created or modified by retro.
|
|
105
110
|
- The fresh-eyes analysis was considered alongside the implementer's context.
|
|
106
111
|
- The merger remains a subsequent step, not work performed by retro.
|
|
@@ -152,22 +152,29 @@ that execs `legion gh -- "$@"`, so `gh …` and `legion gh -- …` are the same
|
|
|
152
152
|
redeems a fresh token from your session's grant — identity is supplied per call, never stored.
|
|
153
153
|
Never run `gh auth login` or `gh auth setup-git`; there is no login state to create. The shim
|
|
154
154
|
refuses `pr merge` (and a raw `gh api …/merge`): no worker role merges a pull request — the merge
|
|
155
|
-
queue does, under its own authority.
|
|
156
|
-
|
|
157
|
-
`
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
155
|
+
queue does, under its own authority. It also refuses every GitHub-issue write — the `issue`
|
|
156
|
+
subcommand's `comment`, `create`, `edit`, `close`, `reopen`, `delete`, `pin`, `unpin`, `transfer`,
|
|
157
|
+
`lock`, `unlock`, and `develop`, and any raw `gh api` call to an `/issues` path whose method is not
|
|
158
|
+
GET (an explicit `-X`, or the POST that `-f`/`-F`/`--input` imply; pull-request conversation
|
|
159
|
+
comments live on that path too, so edit them with `gh pr comment`) — printing
|
|
160
|
+
`Legion issues live on Dispatch; use dispatch_message or dispatch_comment on <your LEGION_ISSUE>`:
|
|
161
|
+
Legion never reads or writes a GitHub issue (LEGION-78). `pr comment`, `pr review`,
|
|
162
|
+
`api …/pulls/…`, `api graphql`, and issue reads are unaffected. The credential reaches `legion`
|
|
163
|
+
through the file `$LEGION_GRANT_FILE` names, written before each of your bash commands by the
|
|
164
|
+
extension; never `cat`, `echo`, copy, or `export` it — `legion credential`, `legion gh`,
|
|
165
|
+
`jj git push`, and `legion handoff complete` read it themselves. The file is the pane's, not the
|
|
166
|
+
command's: a `task` subagent, an `eval` subprocess, or a background job in your pane reads the
|
|
167
|
+
grant your last bash command minted, so its `legion gh` or `jj git push` succeeds only within 60
|
|
168
|
+
seconds of that call and 403s afterwards — a timing artifact, not a broken credential; run
|
|
169
|
+
credentialed commands from your own bash calls.
|
|
163
170
|
|
|
164
171
|
## GitHub PR comment attribution
|
|
165
172
|
|
|
166
173
|
Append this exact structured footer to **every** pull-request comment and review that this
|
|
167
174
|
phase posts on GitHub. It preserves session provenance on the artifact itself so work stays
|
|
168
175
|
attributable to the session that produced it. Dispatch comments carry session provenance
|
|
169
|
-
natively through their own `actor`/`origin` fields; this footer is
|
|
170
|
-
|
|
176
|
+
natively through their own `actor`/`origin` fields; this footer is for GitHub PR artifacts and
|
|
177
|
+
for the retro's Dispatch message (`skills/legion-retro`):
|
|
171
178
|
|
|
172
179
|
```html
|
|
173
180
|
<!-- legion: {"session":"<session-id>","phase":"<phase>"} -->
|
|
@@ -220,7 +227,7 @@ PR opens, and every later phase keeps it current rather than replacing it:
|
|
|
220
227
|
```
|
|
221
228
|
## Verification
|
|
222
229
|
|
|
223
|
-
**CI:** `
|
|
230
|
+
**CI:** `Tests` run <run-id> — jobs lint, pr-title, typecheck, test all success at <head-sha>.
|
|
224
231
|
|
|
225
232
|
**Threads:** <n> resolved, 0 unresolved. Each disposed individually, never in bulk:
|
|
226
233
|
- Thread <id>: fixed in <commit-sha> — <one line>.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sjawhar/pi-legion-envoy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"omp": {
|
|
6
6
|
"extensions": [
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
]
|
|
13
13
|
},
|
|
14
14
|
"legion": {
|
|
15
|
-
"daemonApiVersion":
|
|
15
|
+
"daemonApiVersion": 2
|
|
16
16
|
},
|
|
17
17
|
"repository": {
|
|
18
18
|
"type": "git",
|