@sjawhar/pi-legion 10.15.0 → 10.16.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 +79 -14
- package/dist/skills/legion-architect/SKILL.md +78 -52
- package/dist/skills/legion-controller/SKILL.md +3 -3
- package/dist/skills/legion-retro/SKILL.md +14 -11
- package/dist/skills/legion-worker/SKILL.md +11 -11
- package/dist/skills/legion-worker/references/merge-gate.md +74 -28
- package/dist/skills/legion-worker/references/pr-body.md +2 -2
- package/dist/skills/thermonuclear-code-quality/SKILL.md +96 -185
- package/dist/skills/thermonuclear-deep-review/SKILL.md +45 -69
- package/package.json +2 -2
package/dist/legion.js
CHANGED
|
@@ -14122,6 +14122,16 @@ var DEFAULT_ISSUE_PAGE_LIMIT = 50;
|
|
|
14122
14122
|
var ASK_TURNS = ["human", "agent"];
|
|
14123
14123
|
var DELIVERY_CAPABILITIES = ["aside", "btw", "steer"];
|
|
14124
14124
|
var DELIVERY_DUPLICATE_WINDOW_MS = 72 * 60 * 60 * 1000;
|
|
14125
|
+
var IssueDeployedEventPayloadSchema = exports_external.strictObject({
|
|
14126
|
+
repo: exports_external.string(),
|
|
14127
|
+
number: exports_external.number().int().positive(),
|
|
14128
|
+
run_id: exports_external.number().int().positive(),
|
|
14129
|
+
completed_at: exports_external.iso.datetime()
|
|
14130
|
+
});
|
|
14131
|
+
var DispatchServerStartedPayloadSchema = exports_external.strictObject({
|
|
14132
|
+
commit: exports_external.string().min(1),
|
|
14133
|
+
started_at: exports_external.iso.datetime()
|
|
14134
|
+
});
|
|
14125
14135
|
var DispatchEventSchema = exports_external.object({
|
|
14126
14136
|
issue_key: exports_external.string().nullable(),
|
|
14127
14137
|
artifact_id: exports_external.string().nullish(),
|
|
@@ -15621,11 +15631,11 @@ var LEGION_WORKFLOW_PHASES = [
|
|
|
15621
15631
|
"implementing",
|
|
15622
15632
|
"testing",
|
|
15623
15633
|
"reviewing",
|
|
15634
|
+
"accepting",
|
|
15624
15635
|
"retro",
|
|
15625
15636
|
"merging",
|
|
15626
15637
|
"awaiting_merge",
|
|
15627
15638
|
"integrating",
|
|
15628
|
-
"production_check",
|
|
15629
15639
|
"done",
|
|
15630
15640
|
"held"
|
|
15631
15641
|
];
|
|
@@ -15635,10 +15645,10 @@ var LEGION_PHASE_BACKWARD_TARGETS = [
|
|
|
15635
15645
|
"implementing",
|
|
15636
15646
|
"testing",
|
|
15637
15647
|
"reviewing",
|
|
15648
|
+
"accepting",
|
|
15638
15649
|
"retro",
|
|
15639
15650
|
"merging",
|
|
15640
15651
|
"awaiting_merge",
|
|
15641
|
-
"production_check",
|
|
15642
15652
|
"done",
|
|
15643
15653
|
"held"
|
|
15644
15654
|
];
|
|
@@ -15712,6 +15722,22 @@ var legionSlotView = exports_external.strictObject({
|
|
|
15712
15722
|
admittedAt: timestamp,
|
|
15713
15723
|
lentTo: issueKey.optional()
|
|
15714
15724
|
});
|
|
15725
|
+
var legionCheckWakeView = exports_external.strictObject({
|
|
15726
|
+
kind: nonEmptyString,
|
|
15727
|
+
detail: exports_external.string(),
|
|
15728
|
+
at: timestamp
|
|
15729
|
+
});
|
|
15730
|
+
var legionCheckView = exports_external.strictObject({
|
|
15731
|
+
repo: nonEmptyString,
|
|
15732
|
+
pullRequest: exports_external.number().int().positive(),
|
|
15733
|
+
mergeCommit: exports_external.string(),
|
|
15734
|
+
openedAt: timestamp,
|
|
15735
|
+
looks: exports_external.number().int().nonnegative(),
|
|
15736
|
+
lookOpen: exports_external.boolean(),
|
|
15737
|
+
lastWake: legionCheckWakeView.optional(),
|
|
15738
|
+
nextWakeAt: timestamp,
|
|
15739
|
+
last: exports_external.boolean()
|
|
15740
|
+
});
|
|
15715
15741
|
var legionPendingStatusWrite = exports_external.strictObject({
|
|
15716
15742
|
issue: nonEmptyString,
|
|
15717
15743
|
payload: exports_external.record(exports_external.string(), exports_external.unknown()),
|
|
@@ -15743,7 +15769,8 @@ var legionIssue = exports_external.strictObject({
|
|
|
15743
15769
|
workers: exports_external.partialRecord(exports_external.enum(LEGION_ROLES), legionPhaseView),
|
|
15744
15770
|
pullRequest: legionPullRequestView.optional(),
|
|
15745
15771
|
designGate: legionGateView.optional(),
|
|
15746
|
-
slot: legionSlotView.optional()
|
|
15772
|
+
slot: legionSlotView.optional(),
|
|
15773
|
+
check: legionCheckView.optional()
|
|
15747
15774
|
});
|
|
15748
15775
|
var daemonInfo = exports_external.strictObject({
|
|
15749
15776
|
project: nonEmptyString,
|
|
@@ -15915,7 +15942,7 @@ var LegionHandoffCompleteRequest = exports_external.strictObject({
|
|
|
15915
15942
|
summary: nonEmptyString,
|
|
15916
15943
|
verdict: exports_external.string(),
|
|
15917
15944
|
ready: exports_external.boolean(),
|
|
15918
|
-
commit: nonEmptyString
|
|
15945
|
+
commit: nonEmptyString.optional()
|
|
15919
15946
|
});
|
|
15920
15947
|
var LegionIssueStatusRequest = exports_external.strictObject({
|
|
15921
15948
|
grantId: nonEmptyString,
|
|
@@ -15935,7 +15962,8 @@ var LegionWaveReleaseRequest = exports_external.strictObject({
|
|
|
15935
15962
|
var LegionPhaseBackwardRequest = exports_external.strictObject({
|
|
15936
15963
|
grantId: nonEmptyString,
|
|
15937
15964
|
to: exports_external.enum(LEGION_PHASE_BACKWARD_TARGETS),
|
|
15938
|
-
reason: nonEmptyString
|
|
15965
|
+
reason: nonEmptyString,
|
|
15966
|
+
issue: nonEmptyString.optional()
|
|
15939
15967
|
});
|
|
15940
15968
|
var LegionPhaseRetryRequest = exports_external.strictObject({
|
|
15941
15969
|
grantId: nonEmptyString,
|
|
@@ -16885,7 +16913,7 @@ async function bootLegionClaim(host2) {
|
|
|
16885
16913
|
// package.json
|
|
16886
16914
|
var package_default = {
|
|
16887
16915
|
name: "@sjawhar/pi-legion",
|
|
16888
|
-
version: "10.
|
|
16916
|
+
version: "10.16.1",
|
|
16889
16917
|
license: "Apache-2.0",
|
|
16890
16918
|
type: "module",
|
|
16891
16919
|
omp: {
|
|
@@ -16897,7 +16925,7 @@ var package_default = {
|
|
|
16897
16925
|
]
|
|
16898
16926
|
},
|
|
16899
16927
|
legion: {
|
|
16900
|
-
daemonApiVersion:
|
|
16928
|
+
daemonApiVersion: 33
|
|
16901
16929
|
},
|
|
16902
16930
|
repository: {
|
|
16903
16931
|
type: "git",
|
|
@@ -17320,10 +17348,13 @@ var PhaseOpArguments = exports_external.discriminatedUnion("op", [
|
|
|
17320
17348
|
operation("read_record", { issue: requiredString("read_record", "issue") }),
|
|
17321
17349
|
operation("handoff_complete", {
|
|
17322
17350
|
summary: requiredString("handoff_complete", "summary"),
|
|
17323
|
-
verdict: exports_external.enum(["pass", "
|
|
17351
|
+
verdict: exports_external.enum(["pass", "pending", "fail"], {
|
|
17352
|
+
error: "handoff_complete's verdict is pass, pending or fail"
|
|
17353
|
+
}).optional(),
|
|
17324
17354
|
ready: exports_external.boolean({ error: "handoff_complete's ready is true or false" }).optional()
|
|
17325
17355
|
}),
|
|
17326
17356
|
operation("request_backward_move", {
|
|
17357
|
+
issue: exports_external.string().optional(),
|
|
17327
17358
|
to: exports_external.enum(LEGION_PHASE_BACKWARD_TARGETS, {
|
|
17328
17359
|
error: (issue2) => typeof issue2.input === "string" && issue2.input.trim() !== "" ? `request_backward_move's to is one of ${LEGION_PHASE_BACKWARD_TARGETS.join(", ")}` : "request_backward_move requires to"
|
|
17329
17360
|
}),
|
|
@@ -17356,8 +17387,12 @@ function phaseOpParameters(zod) {
|
|
|
17356
17387
|
const byField = fields;
|
|
17357
17388
|
return byField;
|
|
17358
17389
|
}
|
|
17390
|
+
var PHASE_OP_SENTENCES = {
|
|
17391
|
+
readRecord: "read_record returns the record of `issue` from the daemon's state. ",
|
|
17392
|
+
requestBackwardMove: "request_backward_move returns an issue to an earlier phase (`to`, `reason`): a phase worker moves its own issue; " + "the tree's architect moves an issue in accepting, named by `issue`, back to implementing or testing, which counts a review round. "
|
|
17393
|
+
};
|
|
17359
17394
|
function legionToolDescription(host2) {
|
|
17360
|
-
return "Perform the workflow operation the Legion daemon assigned this role. The daemon advances phases; this tool cannot spawn workers. " + (host2.before ?? "") + "handoff_complete (phase workers and sub-architects; the daemon accepts a completion only from the role working the issue's current phase
|
|
17395
|
+
return "Perform the workflow operation the Legion daemon assigned this role. The daemon advances phases; this tool cannot spawn workers. " + (host2.before ?? "") + "handoff_complete (phase workers and sub-architects; the daemon accepts a completion only from the role working the issue's current phase, " + "and the implementer's report on a look of its closed issue's production check) reports this phase complete: `summary` (two sentences for the " + "architect, or the merger's READY packet), `verdict` pass|fail from the tester alone on a phase completion (the daemon refuses a verdict from " + "any other role there) and pass|pending|fail from the implementer on a production check report (pending: production does not carry the merge " + "yet), `ready: true` for the merger's READY. A handoff is a committed " + "file, `.legion/<issue>/<phase>.json`: the pushed commit carrying it is found here, with the pane's jj, and reported, so write it, commit it and " + "push the branch (`jj git push`) before calling; this call refuses, posting nothing, a handoff still in the working copy, missing, not committed " + "on this branch, carried by another pane's commit, or not yet on `legion/<issue>@origin`, each naming the remedy. A production check report " + "(the task whose first line is `Production check of <KEY>`, handed to the implementer once the merge closed its issue) needs no handoff and no " + "commit: it carries its verdict and summary alone, and the daemon refuses it when no check is open (CHECK_NOT_OPEN). The daemon refuses " + "HANDOFF_NOT_NEW (the commit is the one you reported for your previous phase: write and commit this phase's handoff) and, for READY, each " + "refusal the merger's role names; a refused completion changed nothing, so fix what it names and complete again. An accepted READY whose `note` " + "begins MANUAL_MERGE_REQUIRED: is one a person merges: arm nothing. " + (host2.after ?? "") + `What a later phase needs goes in your handoff; a question for another live role goes to its role topic with ${host2.envoyPublish}.`;
|
|
17361
17396
|
}
|
|
17362
17397
|
async function mintClaimGrant(daemon, claim) {
|
|
17363
17398
|
const { grantId } = await daemon.grant({
|
|
@@ -17393,8 +17428,27 @@ async function executePhaseOp(runtime, input) {
|
|
|
17393
17428
|
case "read_record":
|
|
17394
17429
|
return { record: recordFrom(await daemon.state(), args.issue) };
|
|
17395
17430
|
case "handoff_complete": {
|
|
17396
|
-
const
|
|
17397
|
-
|
|
17431
|
+
const record2 = recordFrom(await daemon.state(), claim.issue);
|
|
17432
|
+
if (claim.role === "implementer" && record2.phase === "done") {
|
|
17433
|
+
if (record2.check !== undefined) {
|
|
17434
|
+
if (args.verdict === undefined) {
|
|
17435
|
+
throw new Error("handoff_complete on a production check names a verdict of pass, pending or fail");
|
|
17436
|
+
}
|
|
17437
|
+
if (args.ready === true) {
|
|
17438
|
+
throw new Error("handoff_complete on a production check is no READY: leave ready out");
|
|
17439
|
+
}
|
|
17440
|
+
}
|
|
17441
|
+
return daemon.handoffComplete({
|
|
17442
|
+
grantId: await mintClaimGrant(daemon, claim),
|
|
17443
|
+
summary: args.summary,
|
|
17444
|
+
verdict: args.verdict ?? "",
|
|
17445
|
+
ready: false
|
|
17446
|
+
});
|
|
17447
|
+
}
|
|
17448
|
+
if (args.verdict === "pending") {
|
|
17449
|
+
throw new Error("handoff_complete's verdict is pass or fail on a phase completion; pending is a production check's");
|
|
17450
|
+
}
|
|
17451
|
+
const commit = await runtime.handoffCommit(record2.phase, claim.role);
|
|
17398
17452
|
return daemon.handoffComplete({
|
|
17399
17453
|
grantId: await mintClaimGrant(daemon, claim),
|
|
17400
17454
|
summary: args.summary,
|
|
@@ -17403,13 +17457,24 @@ async function executePhaseOp(runtime, input) {
|
|
|
17403
17457
|
commit
|
|
17404
17458
|
});
|
|
17405
17459
|
}
|
|
17406
|
-
case "request_backward_move":
|
|
17460
|
+
case "request_backward_move": {
|
|
17461
|
+
let issue2;
|
|
17462
|
+
if (claim.role === "architect") {
|
|
17463
|
+
if (args.issue === undefined || args.issue.trim() === "") {
|
|
17464
|
+
throw new Error("request_backward_move requires issue");
|
|
17465
|
+
}
|
|
17466
|
+
issue2 = args.issue;
|
|
17467
|
+
} else if (args.issue !== undefined && args.issue !== claim.issue) {
|
|
17468
|
+
throw new Error(`a phase worker moves only its own issue ${claim.issue}, not ${args.issue}`);
|
|
17469
|
+
}
|
|
17407
17470
|
await daemon.phaseBackward({
|
|
17408
17471
|
grantId: await mintClaimGrant(daemon, claim),
|
|
17409
17472
|
to: args.to,
|
|
17410
|
-
reason: args.reason
|
|
17473
|
+
reason: args.reason,
|
|
17474
|
+
...issue2 === undefined ? {} : { issue: issue2 }
|
|
17411
17475
|
});
|
|
17412
17476
|
return {};
|
|
17477
|
+
}
|
|
17413
17478
|
}
|
|
17414
17479
|
}
|
|
17415
17480
|
|
|
@@ -17492,7 +17557,7 @@ function createLegionTool(deps) {
|
|
|
17492
17557
|
label: "legion",
|
|
17493
17558
|
description: legionToolDescription({
|
|
17494
17559
|
envoyPublish: "envoy_publish",
|
|
17495
|
-
after: "The controller's read_state returns the daemon's whole state and set_status moves an issue to todo, backlog or icebox. "
|
|
17560
|
+
after: PHASE_OP_SENTENCES.requestBackwardMove + "sign_off is the architect's acceptance of an issue in accepting, moving it to retro; the daemon refuses it in every " + "other phase (SIGNOFF_OUTSIDE_ACCEPTING). " + "The controller's read_state returns the daemon's whole state and set_status moves an issue to todo, backlog or icebox. "
|
|
17496
17561
|
}),
|
|
17497
17562
|
defaultInactive: true,
|
|
17498
17563
|
parameters: toolSchema(pi),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: legion-architect
|
|
3
|
-
description: Own a Legion root or child issue through event-driven decomposition, waves, gates, integration,
|
|
3
|
+
description: Own a Legion root or child issue through event-driven decomposition, waves, gates, integration, acceptance, retro, and the merge that closes it.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Legion Architect
|
|
@@ -15,8 +15,9 @@ separate coordinator to finish necessary work.
|
|
|
15
15
|
- Use the `legion` tool for lifecycle writes. Its issue key is the Dispatch key
|
|
16
16
|
(pattern `^[A-Z][A-Z0-9]*-[0-9]+$`, e.g. `LEGION-41`).
|
|
17
17
|
- The daemon starts every Legion role itself and sequences each issue's phases from its fixed
|
|
18
|
-
workflow table: planner, implementer, tester, reviewer,
|
|
19
|
-
|
|
18
|
+
workflow table: planner, implementer, tester, reviewer, your acceptance (`accepting`, where no
|
|
19
|
+
worker runs), retro (the implementer again), merger, and the merge, which closes the issue; the
|
|
20
|
+
implementer's production check follows the close. One role works an
|
|
20
21
|
issue at a time, and the handoff or event that ends its phase is what starts the next; you start,
|
|
21
22
|
re-assign and order no worker. Message a known phase worker with `envoy_publish` to
|
|
22
23
|
`notifications.role.` followed by its encoded role token. Phase workers escalate lifecycle,
|
|
@@ -221,7 +222,7 @@ reachable, do not have a different running session perform the read on this tree
|
|
|
221
222
|
fold the result into the spec as if it were routine: write the missing capability as its own
|
|
222
223
|
decision block — which external system, which read or write, why the spec needs it — addressed
|
|
223
224
|
to the human, in the same spirit as the implementer's ask to a person when the deployment
|
|
224
|
-
instructions name nobody for a deploy (
|
|
225
|
+
instructions name nobody for a deploy (`skill://legion-worker/references/merge-gate.md`), and leave it
|
|
225
226
|
open until the human resolves it. A workaround substituted for that record only hides the gap
|
|
226
227
|
from the next tree that hits it.
|
|
227
228
|
|
|
@@ -250,12 +251,13 @@ stays out of the workflow.
|
|
|
250
251
|
No notice marks the last child's close as the end-game: each closure arrives as its own
|
|
251
252
|
`child-closed`, and none is a reason to close the parent. Today's daemon does not order the root's
|
|
252
253
|
own phases after its children: when the design gate opens it starts every admitted issue of the
|
|
253
|
-
tree, the root included, so the root's tester can run, and
|
|
254
|
-
child merges
|
|
255
|
-
|
|
256
|
-
child
|
|
257
|
-
`release_children` only
|
|
258
|
-
child has closed, and
|
|
254
|
+
tree, the root included, so the root's tester and reviewer can run, and the root reach `accepting`,
|
|
255
|
+
before any child merges; your acceptance is where the root waits for its children (section 7). To
|
|
256
|
+
get parent integration evidence against the repository's default branch your addressing line names
|
|
257
|
+
after the last child merges, file the parent's integration check as a final child whose acceptance
|
|
258
|
+
is every parent criterion proven on that default branch, release it with `release_children` only
|
|
259
|
+
once every other child has closed, and accept the root (section 6) only after it closes. This holds
|
|
260
|
+
until the redesign's
|
|
259
261
|
integrating phase ships (dispatch://LEGION-223). When that check's tester fails, the daemon sends
|
|
260
262
|
the child back to its implementer; a failure whose fix belongs in other work becomes a new
|
|
261
263
|
corrective child wave you create and release, and the tree returns to children-in-flight. Do not
|
|
@@ -268,9 +270,10 @@ registering its spec or asking for approval again. A child whose old root had no
|
|
|
268
270
|
none, and its catch-up says `handed_over` without `carried`. Its workers already run their phases,
|
|
269
271
|
so its architect decomposes nothing. It reads the root's phase from the catch-up (for a root
|
|
270
272
|
`held`, the phase its `held` notice names). Before `awaiting_merge` it registers its spec, the gate
|
|
271
|
-
its READY waits for, and starts nothing on that gate's `design-approved`;
|
|
272
|
-
`
|
|
273
|
-
|
|
273
|
+
its READY waits for, and starts nothing on that gate's `design-approved`; a child carried in
|
|
274
|
+
`accepting` is yours to accept (section 6). In `awaiting_merge` the READY is already through, so it
|
|
275
|
+
registers no spec and asks no human to approve one: the merge closes the issue, and the production
|
|
276
|
+
check after it is the implementer's. `awaiting_merge` can move back: a red or
|
|
274
277
|
conflicting head returns the root to `implementing` and its next READY reads the gate again, so if
|
|
275
278
|
a later `ready-refused` arrives because the head was sent back, it registers then, as that row
|
|
276
279
|
says. The child stays its own tree after hand-over; reopening the old root later does not take it
|
|
@@ -286,8 +289,8 @@ child wave; a passing result goes on to review and the merge-gate sequence by th
|
|
|
286
289
|
|
|
287
290
|
## 5. Retro
|
|
288
291
|
|
|
289
|
-
Retro is mandatory for every issue that passed review, before merge, and the daemon runs it:
|
|
290
|
-
|
|
292
|
+
Retro is mandatory for every issue that passed review, before the merge, and the daemon runs it:
|
|
293
|
+
when your `sign_off` accepts the issue (section 6), it moves the issue to `retro` and starts the
|
|
291
294
|
implementer on it, in the same agent's session, which stayed live after its phase ended (every
|
|
292
295
|
started role stays live until its issue closes). You start nothing for it. Never `envoy_publish` to
|
|
293
296
|
the implementer's role topic to start retro: the daemon's start is what records which role owns
|
|
@@ -303,18 +306,41 @@ came back empty, or that GitHub refused the body, it has pushed nothing and is s
|
|
|
303
306
|
Answer it with `envoy_publish` to its role topic, saying how from the deployment instructions;
|
|
304
307
|
when they do not say, open a `dispatch ask` naming the missing instruction.
|
|
305
308
|
|
|
306
|
-
## 6.
|
|
309
|
+
## 6. Acceptance and the merge
|
|
307
310
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
311
|
+
The reviewer's approval of the head moves the issue to `accepting` (its Dispatch status stays
|
|
312
|
+
`needs_review`), where no worker runs and the acceptance is yours; the reviewer's `phase-finished`
|
|
313
|
+
notice with an `APPROVED` review is the cue. Read the evidence against every acceptance line of the
|
|
314
|
+
spec and the plan: the tester's handoff (`.legion/<issue>/test.json` at the approved head), the PR
|
|
315
|
+
body's `E2E (implementer)` and `E2E (tester)` lines, the reviewer's approval of that head by SHA
|
|
316
|
+
(`gh api repos/<o>/<r>/pulls/<n>/reviews`), and, for a parent, that every child is `done`
|
|
317
|
+
(section 3). Accept only when scope is fully met, integration evidence is current, corrective work
|
|
318
|
+
is complete, review is clean, and no necessary work was silently deferred. Post the acceptance as a
|
|
319
|
+
`dispatch comment` explicit about that evidence, then accept with `sign_off`, which runs only in
|
|
320
|
+
`accepting` (refused `SIGNOFF_OUTSIDE_ACCEPTING` elsewhere) and moves the issue to `retro` with the
|
|
321
|
+
status `retro`:
|
|
322
|
+
|
|
323
|
+
```text
|
|
324
|
+
dispatch comment --issue LEGION-40 --body-file - <<'EOF'
|
|
325
|
+
<acceptance: scope, integration evidence, the tester's handoff, both E2E lines, the approved head>
|
|
326
|
+
EOF
|
|
327
|
+
legion({ op: "sign_off", issue: "LEGION-40" })
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
What fails goes back, not forward: `request_backward_move` with `issue` (your grant serves the
|
|
331
|
+
tree, so the move names the issue), `to` `implementing` or `testing`, and a `reason` naming what
|
|
332
|
+
fails; the daemon starts that phase's role with the reason in its task and counts a review round,
|
|
333
|
+
as a reviewer's request for changes does. A `handoff_complete` from you in `accepting` is refused
|
|
334
|
+
(`ACCEPTANCE_IS_SIGN_OFF`). CI red at the head in `accepting` returns the issue to `implementing`
|
|
335
|
+
by the daemon's table, as in review. No `Production:` line is asked here: the production check is
|
|
336
|
+
the implementer's after the merge closes the issue (section 7), and nothing after the merge is
|
|
337
|
+
yours.
|
|
313
338
|
|
|
314
339
|
The daemon keeps this order from its fixed table; you start none of its steps:
|
|
315
340
|
|
|
316
341
|
1. tester green and review cycles complete;
|
|
317
342
|
2. on a clean review, the reviewer approves the head by SHA, and the daemon moves the issue to
|
|
343
|
+
`accepting`, where you accept it with `sign_off` or send it back; your `sign_off` moves it to
|
|
318
344
|
`retro`. The approved head still carries the issue's handoffs, `.legion/<issue>/`;
|
|
319
345
|
3. retro commits its learnings under `docs/solutions/` on top of the approved head, then, as the
|
|
320
346
|
one final commit, the removal of `.legion/<issue>/`, so no merge carries a handoff onto the
|
|
@@ -341,17 +367,10 @@ The daemon keeps this order from its fixed table; you start none of its steps:
|
|
|
341
367
|
back to `reviewing`, the move's reason naming the code and what it found, so the reviewer runs a
|
|
342
368
|
new round. A READY refused because the head still carries `.legion/<issue>/` goes back to
|
|
343
369
|
`retro`, so the implementer's retro removes it.
|
|
344
|
-
5. the merge lands under the repository's rules
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
report exists. A defect it finds is a corrective child issue of this tree, not a note on a
|
|
349
|
-
closed one. If the implementer cannot perform the deploy itself, it asks the agent or role the
|
|
350
|
-
deployment instructions name for that deploy (an Envoy role), gives it the merge commit, and
|
|
351
|
-
waits on the production signal the instructions name; the issue waits with it. It opens a
|
|
352
|
-
`dispatch ask` to a person only when the instructions name nobody for that deploy, starting
|
|
353
|
-
with the production gap and why it matters, then naming the required step, its risk, and
|
|
354
|
-
outcome-named options. No one is ever asked for approval to deploy.
|
|
370
|
+
5. the merge lands under the repository's rules and closes the issue (section 7). The implementer's
|
|
371
|
+
production check follows the close, in its own session and on the pull request; you are asked
|
|
372
|
+
nothing for it, and a defect it finds is filed as an issue of its own in the project, never as a
|
|
373
|
+
child of the closed one.
|
|
355
374
|
|
|
356
375
|
What returns the tree to review: a changed diff — a commit above the approved head that
|
|
357
376
|
touches anything outside `docs/solutions/` and the issue's `.legion/<issue>/`, or a
|
|
@@ -366,13 +385,14 @@ never a rebase, since a rebase rewrites every descendant of the chain's fork poi
|
|
|
366
385
|
another tree's branch stacked on it), pushes it as every role pushes (`jj git push`; a genuine
|
|
367
386
|
fast-forward, and a code push, so no `skip-checks: true` line), and posts the before/after fingerprints; the tester re-runs the bare gates only;
|
|
368
387
|
the reviewer confirms and approves the new head by SHA (or continues its round if it had not
|
|
369
|
-
approved); the daemon carries the issue on through retro to the merger, whose new READY packet the daemon posts.
|
|
388
|
+
approved); the daemon carries the issue on through your acceptance and retro to the merger, whose new READY packet the daemon posts.
|
|
370
389
|
This merge happens only when GitHub reports `CONFLICTING`
|
|
371
390
|
(`gh pr view <n> --json mergeable,mergeStateStatus`); read that on every end-game
|
|
372
391
|
wake — `phase-finished`, `catch-up`, `checks-red`, `review-stuck` — because a `CONFLICTING`
|
|
373
392
|
PR gets no CI and no wake announces it. The moment you see it, tell the worker holding the issue's
|
|
374
393
|
phase (`envoy_publish` to its role topic) to move the issue back to `implementing` with
|
|
375
|
-
`request_backward_move`; in `
|
|
394
|
+
`request_backward_move`; in `accepting` the move is yours (`request_backward_move` with `issue`);
|
|
395
|
+
in `awaiting_merge`, where no worker holds a phase, open a `dispatch ask`
|
|
376
396
|
naming the conflict for the human who merges. Do not let the merger send a READY packet for an
|
|
377
397
|
obsolete approval.
|
|
378
398
|
|
|
@@ -387,18 +407,24 @@ role topic, or its review body); the reviewer resolves none.
|
|
|
387
407
|
|
|
388
408
|
## 7. Close
|
|
389
409
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
410
|
+
The merge closes the issue: when GitHub reports the pull request merged while the issue is in
|
|
411
|
+
`awaiting_merge` (a merge before that closes it the moment it gets there), the daemon writes
|
|
412
|
+
`done`. A child's close reaches you as `child-closed`; the daemon retires five of its six claims,
|
|
413
|
+
suspends its implementer with its session kept, and sets its Sandbox `Suspended` with its volume,
|
|
414
|
+
because the implementer still owes the production check: handed to it in its live session at the
|
|
415
|
+
merge, it is work after the close that holds no pod while it waits for the deploy
|
|
416
|
+
(`skill://legion-worker/references/merge-gate.md`, *The production check after the close*). A
|
|
417
|
+
root's close writes `done`, frees the tree's admission slot, and lingers the tree for
|
|
418
|
+
`linger_hours` with every member suspended; a child still in the workflow at the end of the linger
|
|
419
|
+
is handed over (section 3), and the tree's close waits for a member's open check.
|
|
420
|
+
|
|
421
|
+
Nothing of the check is yours: you post no sign-off after the merge and wait for no record. Its
|
|
422
|
+
record lives on the pull request alone, since Dispatch refuses a write on a closed issue; a defect
|
|
423
|
+
it finds is filed as an issue of its own in the project, never a child of the closed one; and
|
|
424
|
+
`read_record` shows a closed issue's open check (`check`: the pull request, the merge commit, the
|
|
425
|
+
looks, what woke the last one and when the next is due). A parent waits in `accepting` until
|
|
426
|
+
every child is `done` (section 3): accepted earlier it would merge before its children, the state
|
|
427
|
+
dispatch://LEGION-703 has the daemon rule out.
|
|
402
428
|
|
|
403
429
|
## Wake routing
|
|
404
430
|
|
|
@@ -418,13 +444,13 @@ active phase worker.
|
|
|
418
444
|
| `child-not-recorded` | Its `reason` names a child your tree did not record and why: its repository resolves to none (neither it nor an ancestor names one, and the project has no default), or to one not mapped to the project, or to one the daemon leaves out for a missing GitHub App. The daemon said why on the child too. Set the child's repository to one of the project's (`dispatch issue-update --repo owner/name`), then release it again with `release_children`; tell the controller when the repository it needs is not served. |
|
|
419
445
|
| `design-approved` | Its `version` is the approved one. A human approved the root spec document at its current version; the gate is open. Proceed to section 2 — except on a root whose catch-up said `handed_over`: its workers already run their phases, so start nothing; the approval releases any READY the gate refused. One whose `version` is the version a human's own answer to a decision block wrote, after an earlier approval (Dispatch carried that approval to it, section 1), opens nothing new: carry on, and release no wave because of it. |
|
|
420
446
|
| `design-changes-requested` | Its `version` and `reason`. A human asked for changes to the root spec at `version`, for `reason`. Revise the spec as the reason asks and request approval again as section 1 says; stay parked; the gate is closed. |
|
|
421
|
-
| `phase-finished` | The daemon has already moved the issue to its next phase by its fixed table and started that phase's role; you start nothing. Read the committed handoff for the finishing phase; if it shows unresolved gaps, tell the role now working the issue (`envoy_publish` to its role topic). A `planner` notice that names a departure from the spec's design defers to section 1's full condition: when the approved Summary, Acceptance, scope and settled decisions still hold, the plan is the record; otherwise change the root spec and request approval again as section 1 says. A `reviewer` notice whose GitHub review is `CHANGES_REQUESTED` needs nothing from you: the daemon has returned the issue to `implementing` (Dispatch `in_progress`) and started the **implementer**, whose correction goes through the tester and the reviewer again, never straight to retro. A `reviewer` notice with an `APPROVED` review means the
|
|
422
|
-
| `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 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. 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 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` 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. |
|
|
423
|
-
| `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
|
|
447
|
+
| `phase-finished` | The daemon has already moved the issue to its next phase by its fixed table and started that phase's role; you start nothing. Read the committed handoff for the finishing phase; if it shows unresolved gaps, tell the role now working the issue (`envoy_publish` to its role topic). A `planner` notice that names a departure from the spec's design defers to section 1's full condition: when the approved Summary, Acceptance, scope and settled decisions still hold, the plan is the record; otherwise change the root spec and request approval again as section 1 says. A `reviewer` notice whose GitHub review is `CHANGES_REQUESTED` needs nothing from you: the daemon has returned the issue to `implementing` (Dispatch `in_progress`) and started the **implementer**, whose correction goes through the tester and the reviewer again, never straight to retro. A `reviewer` notice with an `APPROVED` review means the issue is in `accepting` and the acceptance is yours (section 6): read the evidence, then `sign_off` or send it back with `request_backward_move`. A `tester` notice with `verdict: "fail"` — its handoff carries `implementerProof.verdict: "rejected"`, or a failure naming the production-like proof — has gone back to the **implementer** by the daemon's table; never supply the proof from another role. A worker that reports no surface reaches the changed path sends that report instead of completing its phase, so the daemon starts nothing more on that issue and the worker stays idle in its session, not suspended: file a child issue in this tree to build the surface (infrastructure, tooling, or a skill), and when that child's `child-closed` arrives, tell the waiting worker to continue with `envoy_publish` to its role topic. That report is never a reason to advance the phase. |
|
|
448
|
+
| `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 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` 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. |
|
|
449
|
+
| `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 merge closes the issue the moment it reaches `awaiting_merge` (section 7), and you start nothing; the production check after the close is the implementer's, on the pull request, and asks nothing of you. Until then the phases run as they would, your acceptance included. |
|
|
424
450
|
| `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. |
|
|
425
|
-
| `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
|
|
426
|
-
| `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
|
|
427
|
-
| `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
|
|
451
|
+
| `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. |
|
|
452
|
+
| `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. |
|
|
453
|
+
| `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. |
|
|
428
454
|
| 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. |
|
|
429
455
|
| `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. |
|
|
430
456
|
| `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. |
|
|
@@ -269,9 +269,9 @@ leans on `External links:`, and the label row is the one that never depends on h
|
|
|
269
269
|
|
|
270
270
|
- `OPEN`: work in flight. Skip it; the issue stays where it is.
|
|
271
271
|
- `MERGED`: the change landed. Take nothing and set nothing: an issue Legion ran is already skipped
|
|
272
|
-
by the `Legion ran it without the label now on it` row, and the daemon
|
|
273
|
-
|
|
274
|
-
the issue left open. Skip it and name it in your summary
|
|
272
|
+
by the `Legion ran it without the label now on it` row, and the daemon closes such an issue
|
|
273
|
+
itself on the merge event (its implementer's production check follows the close); one Legion did
|
|
274
|
+
not run is someone's finished work with the issue left open. Skip it and name it in your summary
|
|
275
275
|
(`<KEY>: pull request <url> merged, the issue is still todo`) so a person closes it.
|
|
276
276
|
- `CLOSED` (not merged): someone abandoned the change. Skip it and name it in your summary; the
|
|
277
277
|
issue's owner decides what follows. For an issue Legion runs, the daemon tells its architect
|
|
@@ -20,7 +20,9 @@ retrospective's durable output.
|
|
|
20
20
|
|
|
21
21
|
1. Tester green and all code-review cycles finish.
|
|
22
22
|
2. The reviewer approves the head. It still carries the issue's handoffs, `.legion/<issue>/`
|
|
23
|
-
(`<issue>` is your `LEGION_ISSUE`), which every tester and reviewer round writes again.
|
|
23
|
+
(`<issue>` is your `LEGION_ISSUE`), which every tester and reviewer round writes again. The
|
|
24
|
+
architect then accepts the issue (`accepting`) with `sign_off`, which starts this retro; one it
|
|
25
|
+
sends back to `implementing` or `testing` instead runs those phases again first.
|
|
24
26
|
3. Run this retro (*Durable outputs*, below): its learnings under `docs/solutions/`, then one final
|
|
25
27
|
commit removing `.legion/<issue>/`, pushed together, and the retro message on the Dispatch
|
|
26
28
|
issue. Retro writes **no handoff**. The removal exists because a squash merge commits the head
|
|
@@ -32,13 +34,14 @@ retrospective's durable output.
|
|
|
32
34
|
posts it on the Dispatch issue and publishes it to the project's merge-queue role when one is
|
|
33
35
|
configured. On READY's acceptance the daemon submits the merge, and it lands under the
|
|
34
36
|
repository's rules: its required reviews and checks, and its merge queue when it enables one.
|
|
35
|
-
5.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
The record is the pull request's `Production:` line
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
5. That merge closes the issue: the daemon writes `done`, and the implementer — not the reviewer or
|
|
38
|
+
merger — is handed the production check in its live session, as work after the close: the agent
|
|
39
|
+
that developed the change is responsible for testing it in production, once production carries
|
|
40
|
+
the merge. The record is the pull request's `Production:` line and one pull-request comment,
|
|
41
|
+
naming what was driven, how, what was observed, the merge commit and what woke the look; nothing
|
|
42
|
+
is written on the closed issue, since Dispatch refuses it. A defect the check finds is filed as
|
|
43
|
+
an issue of its own in the project, never as a child of the closed one
|
|
44
|
+
(`skill://legion-worker/references/merge-gate.md`, *The production check after the close*).
|
|
42
45
|
|
|
43
46
|
Retro's commits sit above the reviewer's approved head and the approval stands: commits that change
|
|
44
47
|
only `docs/solutions/`, and the one that removes `.legion/<issue>/`, do not void it, and the tree
|
|
@@ -47,7 +50,7 @@ the approved head with no review round after it brings the base's paths above th
|
|
|
47
50
|
refuses it (`READY_UNREVIEWED_CHANGE`) and the reviewer reviews the new head as a new round; one the
|
|
48
51
|
conflict procedure sends back through `implementing` is confirmed before READY. Any round after
|
|
49
52
|
retro (a conflict round, a round a red CI sends back, a re-review) writes `.legion/<issue>/` again,
|
|
50
|
-
and every approval moves the issue to retro once more: each retro ends with the removal again, or
|
|
53
|
+
and every approval, through the architect's acceptance, moves the issue to retro once more: each retro ends with the removal again, or
|
|
51
54
|
READY is refused.
|
|
52
55
|
|
|
53
56
|
Do not start retro before step 2 or skip it because the change seems mechanical; the merger's
|
|
@@ -174,7 +177,7 @@ Dispatch message on
|
|
|
174
177
|
the issue — `--issue` is your `LEGION_ISSUE`; Legion issues live on Dispatch, never on a GitHub
|
|
175
178
|
issue, so no `gh issue` write and no GitHub-issue comment — naming the documents, the
|
|
176
179
|
one-to-three most useful takeaways, the two proofs you read, and the production check that
|
|
177
|
-
follows the merge. The message must carry this revived implementer's structured
|
|
180
|
+
follows the merge's close of the issue. The message must carry this revived implementer's structured
|
|
178
181
|
attribution footer with `phase` set to `retro`; the body is capped at 2,000 characters:
|
|
179
182
|
|
|
180
183
|
```bash
|
|
@@ -189,7 +192,7 @@ dispatch message --issue <KEY> --body-file - <<'EOF'
|
|
|
189
192
|
|
|
190
193
|
**Proofs read:** implementer <surface/command>, tester <surface/command>.
|
|
191
194
|
|
|
192
|
-
**Production check:** <what the implementer will drive
|
|
195
|
+
**Production check:** <what the implementer will drive in production once the merge closes the issue and production carries it, and the deploy or restart that carries it there, with the agent or role the deployment instructions name to perform it, or the person it asks when they name none; no reply is awaited, the daemon wakes the check on the deploy's event>
|
|
193
196
|
|
|
194
197
|
<!-- legion: {"session":"<session-id>","phase":"retro"} -->
|
|
195
198
|
EOF
|
|
@@ -88,8 +88,8 @@ into the branch only on a real conflict or a retarget
|
|
|
88
88
|
implementer's own proof on a production-like surface at the head that merges, an applied simplify
|
|
89
89
|
head included (`skill://legion-worker/references/pr-body.md#what-a-proof-is`,
|
|
90
90
|
`skill://legion-worker/references/pr-body.md#the-rules-every-phases-evidence-follows`); and the
|
|
91
|
-
implementer's production check after the merge
|
|
92
|
-
(`skill://legion-worker/references/merge-gate.md#after-the-
|
|
91
|
+
implementer's production check after the merge closes the issue
|
|
92
|
+
(`skill://legion-worker/references/merge-gate.md#the-production-check-after-the-close`).
|
|
93
93
|
|
|
94
94
|
## Asking another role
|
|
95
95
|
|
|
@@ -304,12 +304,12 @@ line), the full definition of a proof, what the tester verifies, and the simplif
|
|
|
304
304
|
forward merge and never `jj rebase`; the unchanged-diff fingerprint each role compares
|
|
305
305
|
afterwards is in the same reference: `skill://legion-worker/references/conflicts-and-rewrites.md`.
|
|
306
306
|
- **The merge gate**, in order: the tester's evidence green → the reviewer's approval of the head
|
|
307
|
-
→ retro → the merger's READY → the merge
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
repository's required reviews and
|
|
311
|
-
(`skill://legion-worker/references/merge-gate.md`). The reviewer's
|
|
312
|
-
the
|
|
307
|
+
→ the architect's acceptance → retro → the merger's READY → the merge, which closes the issue →
|
|
308
|
+
the implementer's production check, after the close. On READY's acceptance the daemon submits
|
|
309
|
+
the merge of the head READY named, unless the answer's note begins `MANUAL_MERGE_REQUIRED:`,
|
|
310
|
+
where a person merges it; no role runs `gh pr merge`, and the repository's required reviews and
|
|
311
|
+
checks decide when it lands (`skill://legion-worker/references/merge-gate.md`). The reviewer's
|
|
312
|
+
submissions, the acceptance, retro's commit, READY, the daemon's submission, and the check follow the same reference.
|
|
313
313
|
- **No surface reaches the changed path** is a report to the architect, never a reason to
|
|
314
314
|
complete the phase: `skill://legion-worker/references/pr-body.md`.
|
|
315
315
|
|
|
@@ -370,9 +370,9 @@ check, or the `MANUAL_MERGE_REQUIRED:` note of a pull request a person merges).
|
|
|
370
370
|
|
|
371
371
|
Retro's last commit removes `.legion/<issue>/` from the head that merges (`skill://legion-retro`);
|
|
372
372
|
a round after it, such as one a withdrawn READY sends back, writes and commits its own handoff
|
|
373
|
-
again, since `handoff_complete` reports the commit carrying the file. Retro
|
|
374
|
-
|
|
375
|
-
with `handoff_complete`
|
|
373
|
+
again, since `handoff_complete` reports the commit carrying the file. Retro and the merger write no
|
|
374
|
+
`.legion/<issue>/<phase>.json`, commit no handoff, and report with `handoff_complete` alone, which
|
|
375
|
+
reports the commit the workspace stands on; a look of the production check after the close reports with `handoff_complete` and a `verdict`, and no commit at all.
|
|
376
376
|
|
|
377
377
|
**Every role pushes its own commits.** After the handoff commit — and, for the tester, the red
|
|
378
378
|
tests it wrote — push the issue branch with the plain `jj` of your pane, from bash in your
|