@sjawhar/claude-envoy 1.12.1 → 1.14.0
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/.claude-plugin/plugin.json +1 -1
- package/.omp-plugin/plugin.json +1 -1
- package/dist/dispatch.js +58 -27
- package/dist/envoy-channel.js +111 -55
- package/dist/session-hook.js +53 -23
- package/legion/skills/legion-architect/SKILL.md +10 -8
- package/legion/skills/legion-architect/references/acceptance.md +1 -1
- package/legion/skills/legion-architect/references/decomposition.md +27 -18
- package/legion/skills/legion-architect/references/design-gate.md +4 -4
- package/legion/skills/legion-architect/references/wake-routing.md +5 -5
- package/legion/skills/legion-controller/SKILL.md +5 -2
- package/legion/skills/legion-controller/references/daily-report.md +7 -0
- package/legion/skills/legion-controller/references/weekly-audit.md +146 -0
- package/package.json +1 -1
- package/skills/dispatch/SKILL.md +2 -2
- package/skills/dispatch-first/SKILL.md +3 -7
- package/skills/dispatch-writing-specs/SKILL.md +1 -1
- package/src/session-title.ts +3 -42
package/dist/session-hook.js
CHANGED
|
@@ -13661,7 +13661,8 @@ var IssueEventPayloadSchema = object({
|
|
|
13661
13661
|
route: string2().nullish(),
|
|
13662
13662
|
changed: array(string2()).optional(),
|
|
13663
13663
|
ancestors: array(string2()).optional(),
|
|
13664
|
-
thread_logins: array(string2()).optional()
|
|
13664
|
+
thread_logins: array(string2()).optional(),
|
|
13665
|
+
forced: object({ past: array(string2()) }).optional()
|
|
13665
13666
|
});
|
|
13666
13667
|
var ArtifactCreatedEventPayloadSchema = object({
|
|
13667
13668
|
artifact: object({ id: string2().optional(), slug: string2().optional(), name: string2().optional() }).optional()
|
|
@@ -14614,12 +14615,12 @@ var dispatchToolSpecs = [
|
|
|
14614
14615
|
{
|
|
14615
14616
|
name: "dispatch_related",
|
|
14616
14617
|
example: { project: "CORE", title: "Retry failed webhook deliveries" },
|
|
14617
|
-
description: "Run the related-item check that dispatch issue, dispatch ask and a new decision block in " + "dispatch doc-edit run on their own write, and write nothing: it lists the three items of " + "one project most like a title and a text (at most one per issue or document, open ones " + "first), each ask with whether it is answered, and a past decision that already answers " + "the same question. Use it to check a draft before filing it. It searches by meaning and by " + "the text's rarest words, and says how it searched; when meaning search cannot run it " + "searches by keywords only and says why. When it could not search at all it says why and " + "names the dispatch search to run instead.
|
|
14618
|
+
description: "Run the related-item check that dispatch issue, dispatch ask and a new decision block in " + "dispatch doc-edit run on their own write, and write nothing: it lists the three items of " + "one project most like a title and a text (at most one per issue or document, open ones " + "first), each ask with whether it is answered, and a past decision that already answers " + "the same question. Use it to check a draft before filing it. It searches by meaning and by " + "the text's rarest words, and says how it searched; when meaning search cannot run it " + "searches by keywords only and says why. When it could not search at all it says why and " + "names the dispatch search to run instead. exclude_issues leaves each named issue's own " + "items out of the results, at most 50 of them, so a draft under a parent can leave out " + "that parent and its children without changing how rare the text's words are; " + "keywords_only skips meaning search.",
|
|
14618
14619
|
arguments: (z) => ({
|
|
14619
14620
|
project: z.string().describe("Project key to search within, such as CORE."),
|
|
14620
14621
|
title: z.string({ max: ISSUE_TITLE_MAX }).describe("Optional title to check, as an issue title; at most 1,000 characters.").optional(),
|
|
14621
14622
|
text: z.string().describe("Optional text to check, such as a draft spec or a question.").optional(),
|
|
14622
|
-
|
|
14623
|
+
exclude_issues: z.array(z.string()).describe("Optional issue keys in the project whose own items are left out of the results; at most 50.").optional(),
|
|
14623
14624
|
keywords_only: z.boolean().describe("Search by keywords only, skipping meaning search.").optional()
|
|
14624
14625
|
}),
|
|
14625
14626
|
validation: {
|
|
@@ -16569,9 +16570,12 @@ async function tryExec(exec, file, args, cwd, limitMs) {
|
|
|
16569
16570
|
exec(file, args, { cwd, signal: controller.signal }),
|
|
16570
16571
|
expired.promise
|
|
16571
16572
|
]);
|
|
16572
|
-
|
|
16573
|
+
if (answer === null) {
|
|
16574
|
+
return { failure: "timedOut", ok: false };
|
|
16575
|
+
}
|
|
16576
|
+
return { ok: true, stdout: answer.stdout };
|
|
16573
16577
|
} catch {
|
|
16574
|
-
return
|
|
16578
|
+
return { failure: "refused", ok: false };
|
|
16575
16579
|
} finally {
|
|
16576
16580
|
clearTimeout(timer);
|
|
16577
16581
|
}
|
|
@@ -16612,13 +16616,13 @@ function parseGitHubRemoteUrl(url) {
|
|
|
16612
16616
|
return null;
|
|
16613
16617
|
}
|
|
16614
16618
|
async function resolveCwdRepo(cwd, exec) {
|
|
16615
|
-
const
|
|
16616
|
-
if (
|
|
16617
|
-
const url = selectRemoteUrl(parseRemoteList(
|
|
16619
|
+
const remoteList = await tryExec(exec, "jj", ["git", "remote", "list"], cwd, REMOTE_LOOKUP_MS);
|
|
16620
|
+
if (remoteList.ok) {
|
|
16621
|
+
const url = selectRemoteUrl(parseRemoteList(remoteList.stdout));
|
|
16618
16622
|
return url ? parseGitHubRemoteUrl(url) : null;
|
|
16619
16623
|
}
|
|
16620
|
-
const
|
|
16621
|
-
return
|
|
16624
|
+
const originRemote = await tryExec(exec, "git", ["remote", "get-url", "origin"], cwd, REMOTE_LOOKUP_MS);
|
|
16625
|
+
return originRemote.ok ? parseGitHubRemoteUrl(originRemote.stdout) : null;
|
|
16622
16626
|
}
|
|
16623
16627
|
async function resolveOrigin(env, exec, cwd) {
|
|
16624
16628
|
const origin = {
|
|
@@ -16627,8 +16631,8 @@ async function resolveOrigin(env, exec, cwd) {
|
|
|
16627
16631
|
};
|
|
16628
16632
|
const pane = env.TMUX_PANE;
|
|
16629
16633
|
if (pane) {
|
|
16630
|
-
const
|
|
16631
|
-
const [target, paneId] =
|
|
16634
|
+
const answer = await tryExec(exec, "tmux", ["display-message", "-p", "-t", pane, "#S:#I.#P #{pane_id}"], cwd, TMUX_PANE_LOOKUP_MS);
|
|
16635
|
+
const [target, paneId] = answer.ok ? answer.stdout.trim().split(" ") : [];
|
|
16632
16636
|
if (target)
|
|
16633
16637
|
origin.tmux = target;
|
|
16634
16638
|
if (paneId)
|
|
@@ -19544,11 +19548,19 @@ function relatedCheckLines(suggestions, context) {
|
|
|
19544
19548
|
lines.push(`A past decision may already answer this: ${suggestionLabel(decision)}, answered by ${decision.answered_by}${when} \u2192 ${absoluteHref(decision.href, context.configUrl)}`);
|
|
19545
19549
|
lines.push(...askLines(decision.ask));
|
|
19546
19550
|
}
|
|
19551
|
+
const likely = suggestions.likely_duplicate;
|
|
19552
|
+
if (likely !== undefined && likely !== null) {
|
|
19553
|
+
lines.push(`Likely duplicate: ${likely.key} [${likely.status}] ${likely.title} \u2192 ${absoluteHref(likely.href, context.configUrl)}`);
|
|
19554
|
+
lines.push("Read it. If it is the same work, fold this issue into it by the duplicate rule of skill://dispatch-first.");
|
|
19555
|
+
}
|
|
19556
|
+
if (suggestions.near_copy_check !== undefined && suggestions.near_copy_check !== "") {
|
|
19557
|
+
lines.push(`No near-copy check was made: ${suggestions.near_copy_check}.`);
|
|
19558
|
+
}
|
|
19547
19559
|
for (const match of suggestions.title_matches ?? []) {
|
|
19548
|
-
lines.push(`
|
|
19560
|
+
lines.push(`Near copy you forced past: ${match.key} [${match.status}] ${match.title} \u2192 ${absoluteHref(match.href, context.configUrl)}`);
|
|
19549
19561
|
}
|
|
19550
19562
|
} catch (error) {
|
|
19551
|
-
lines.push(`The check's past decision or
|
|
19563
|
+
lines.push(`The check's past decision or near copies could not be shown: ${messageFor(error)}`);
|
|
19552
19564
|
}
|
|
19553
19565
|
return lines;
|
|
19554
19566
|
}
|
|
@@ -21431,7 +21443,13 @@ function eventHead(event) {
|
|
|
21431
21443
|
return textHead(`${event.payload.name} v${event.payload.version.number}${event.payload.version.summary ? `: ${event.payload.version.summary}` : ""}`);
|
|
21432
21444
|
case "artifact.human_edited":
|
|
21433
21445
|
return textHead(humanEditHead(event.payload));
|
|
21434
|
-
case "issue.created":
|
|
21446
|
+
case "issue.created": {
|
|
21447
|
+
const forced = event.payload.forced;
|
|
21448
|
+
const head = `status ${event.payload.status}`;
|
|
21449
|
+
if (forced === undefined)
|
|
21450
|
+
return head;
|
|
21451
|
+
return forced.past.length === 0 ? `${head} \xB7 forced, the near-copy check did not answer` : `${head} \xB7 forced past ${forced.past.join(", ")}`;
|
|
21452
|
+
}
|
|
21435
21453
|
case "issue.updated":
|
|
21436
21454
|
case "issue.closed":
|
|
21437
21455
|
return `status ${event.payload.status}`;
|
|
@@ -21447,10 +21465,14 @@ function eventRef(event) {
|
|
|
21447
21465
|
const kind = event.type.startsWith("message.") ? "message" : event.type.startsWith("comment.") || event.type.startsWith("suggestion.") ? "comment" : event.type.startsWith("ask.") ? "ask" : undefined;
|
|
21448
21466
|
return kind === undefined ? undefined : `dispatch://${event.issue_key}/${kind}/${id}`;
|
|
21449
21467
|
}
|
|
21468
|
+
function eventActorText(actor) {
|
|
21469
|
+
const title = actor.kind === "session" ? actor.origin?.session_title : undefined;
|
|
21470
|
+
return title === undefined || title === "" ? actorText(actor) : `${actorText(actor)} (${title})`;
|
|
21471
|
+
}
|
|
21450
21472
|
function eventLine(event) {
|
|
21451
21473
|
const head = eventHead(event);
|
|
21452
21474
|
const ref = eventRef(event);
|
|
21453
|
-
return `- #${event.seq} ${event.type}${ref === undefined ? "" : ` ${ref}`} \xB7 ${
|
|
21475
|
+
return `- #${event.seq} ${event.type}${ref === undefined ? "" : ` ${ref}`} \xB7 ${eventActorText(event.actor)} \xB7 ${event.created_at}${head === undefined || head === "" ? "" : ` \xB7 ${head}`}`;
|
|
21454
21476
|
}
|
|
21455
21477
|
function childrenSummary(issue) {
|
|
21456
21478
|
return [
|
|
@@ -22267,15 +22289,17 @@ async function executeDispatchTool(input) {
|
|
|
22267
22289
|
const candidates = duplicateCandidates(error);
|
|
22268
22290
|
return {
|
|
22269
22291
|
text: [
|
|
22270
|
-
`Not created: "${title}"
|
|
22292
|
+
`Not created: "${title}" is a near copy of an open issue.`,
|
|
22271
22293
|
...candidates.map((candidate) => {
|
|
22272
22294
|
const href = new URL(candidate.href, linkBase).toString();
|
|
22273
22295
|
return `${candidate.key} [${candidate.status}] ${candidate.title} \u2192 ${href}`;
|
|
22274
22296
|
}),
|
|
22275
|
-
"
|
|
22297
|
+
"Read each issue above. If one is the same work, fold this into it by the duplicate rule of skill://dispatch-first.",
|
|
22298
|
+
"If none is, run dispatch issue again with --force, and write in the new issue's spec why it is not each issue named here."
|
|
22276
22299
|
].join(`
|
|
22277
22300
|
`),
|
|
22278
|
-
details: { duplicates: candidates }
|
|
22301
|
+
details: { duplicates: candidates },
|
|
22302
|
+
refused: true
|
|
22279
22303
|
};
|
|
22280
22304
|
}
|
|
22281
22305
|
}
|
|
@@ -22443,12 +22467,12 @@ async function executeDispatchTool(input) {
|
|
|
22443
22467
|
const project = stringArg(args, "project");
|
|
22444
22468
|
const title = optionalString(args, "title");
|
|
22445
22469
|
const text = optionalString(args, "text");
|
|
22446
|
-
const
|
|
22470
|
+
const excludeIssues = optionalStringArray(args, "exclude_issues");
|
|
22447
22471
|
const keywordsOnly = optionalBoolean(args, "keywords_only");
|
|
22448
22472
|
const suggestions = await client.related(project, {
|
|
22449
22473
|
...title === undefined ? {} : { title },
|
|
22450
22474
|
...text === undefined ? {} : { text },
|
|
22451
|
-
...
|
|
22475
|
+
...excludeIssues === undefined ? {} : { exclude_issues: excludeIssues },
|
|
22452
22476
|
...keywordsOnly === undefined ? {} : { keywords_only: keywordsOnly }
|
|
22453
22477
|
});
|
|
22454
22478
|
return {
|
|
@@ -26411,6 +26435,7 @@ function dispatchTarget(env, config) {
|
|
|
26411
26435
|
var DEFAULT_ENVOY_URL = "http://127.0.0.1:9020";
|
|
26412
26436
|
var DEFAULT_HEARTBEAT_MS = SESSION_HEARTBEAT_MS;
|
|
26413
26437
|
var MIN_PASS_DEADLINE_MS = 30000;
|
|
26438
|
+
var DEFAULT_TMUX_PLACE_CACHE_MS = 30000;
|
|
26414
26439
|
var MIN_INTERVAL_MS = 25;
|
|
26415
26440
|
var MAX_INTERVAL_MS = 2147483647;
|
|
26416
26441
|
var REGISTRATION_SETTLE_MS = 5000;
|
|
@@ -26424,6 +26449,10 @@ function namedInterval(raw) {
|
|
|
26424
26449
|
const parsed = Number(raw);
|
|
26425
26450
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : undefined;
|
|
26426
26451
|
}
|
|
26452
|
+
function resolvedInterval(raw, fallback) {
|
|
26453
|
+
const named = namedInterval(raw);
|
|
26454
|
+
return named === undefined ? fallback : Math.min(Math.max(named, MIN_INTERVAL_MS), MAX_INTERVAL_MS);
|
|
26455
|
+
}
|
|
26427
26456
|
function natsSeedFromEnvironment(env) {
|
|
26428
26457
|
const { NATS_NKEY_SEED_FILE: file, NATS_NKEY_SEED: plain } = env;
|
|
26429
26458
|
if (file !== undefined) {
|
|
@@ -26514,8 +26543,8 @@ function envoySettings(env, config, options = {}) {
|
|
|
26514
26543
|
inbound = { off: messageFor(error) };
|
|
26515
26544
|
}
|
|
26516
26545
|
}
|
|
26517
|
-
const
|
|
26518
|
-
const
|
|
26546
|
+
const heartbeatMs = resolvedInterval(env.ENVOY_HEARTBEAT_MS, DEFAULT_HEARTBEAT_MS);
|
|
26547
|
+
const tmuxPlaceCacheMs = resolvedInterval(env.ENVOY_TMUX_PLACE_CACHE_MS, DEFAULT_TMUX_PLACE_CACHE_MS);
|
|
26519
26548
|
const namedPassDeadlineMs = namedInterval(env.ENVOY_PASS_DEADLINE_MS);
|
|
26520
26549
|
return {
|
|
26521
26550
|
enabled: true,
|
|
@@ -26523,6 +26552,7 @@ function envoySettings(env, config, options = {}) {
|
|
|
26523
26552
|
credential,
|
|
26524
26553
|
inbound,
|
|
26525
26554
|
heartbeatMs,
|
|
26555
|
+
tmuxPlaceCacheMs,
|
|
26526
26556
|
passDeadlineMs: Math.min(Math.max(namedPassDeadlineMs ?? MIN_PASS_DEADLINE_MS, heartbeatMs), MAX_INTERVAL_MS),
|
|
26527
26557
|
passDeadlineRaisedFrom: namedPassDeadlineMs !== undefined && namedPassDeadlineMs < heartbeatMs ? namedPassDeadlineMs : undefined
|
|
26528
26558
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: legion-architect
|
|
3
|
-
description: "Use when you are a Legion root or sub-architect: decomposing children, gating the root spec, releasing
|
|
3
|
+
description: "Use when you are a Legion root or sub-architect: decomposing children, gating the root spec, releasing children, accepting a reviewed head, or handling a delivered wake or a worker's escalation. Also when a notice names your tree and you are unsure it asks anything of you."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Legion architect
|
|
@@ -24,26 +24,28 @@ You own one issue tree from its first decision to the merge that closes it. The
|
|
|
24
24
|
## 1. Decompose or adopt
|
|
25
25
|
|
|
26
26
|
- Read a child's handoff, create children, split across repositories or attach architecture components as `skill://legion-architect/references/decomposition.md` says.
|
|
27
|
-
- Adopt children a human created and release them
|
|
27
|
+
- Adopt children a human created and release them at once. Never replace or re-decompose them.
|
|
28
28
|
- Choose a single-issue tree only when one pull request in one repository can deliver every criterion.
|
|
29
|
-
- A child issue
|
|
29
|
+
- A child issue needs a repository or a deploy order of its own: its code is in a repository no open pull request of this tree is in, or it must be applied on its own before other work of the tree can build on it. A repeated change — the same edit per program, directory or instance — is one child, never one per instance. Never use a child to track a to-do, a fix found in review, a proof of the root's acceptance, or a part of your own work: do it in this tree. Each child costs a workspace, an admission wait and a review loop. Before any `dispatch issue` that creates a child, and before `release_children`, run the red-team check in `skill://legion-architect/references/decomposition.md`.
|
|
30
|
+
- Work in a repository this tree already has a pull request in, which waits on a sibling's merge, is no child and is not parked in `backlog`: write it into the spec of the issue whose pull request carries it — the parent's, or the sibling's when the parent's pull request is in another repository — so one pull request lands it in order.
|
|
31
|
+
- A child that duplicates another issue and is not yet released is never released: fold it by the duplicate rule of `skill://dispatch-first`. The daemon records a child at its release and re-asserts a status only on an issue it has recorded, so that close stands. A released duplicate goes out with `park_child` instead.
|
|
30
32
|
- Every child names the running system its criteria are proven on (the command, service or page the tester drives) and the repository skill that drives it. If the repository cannot exercise a criterion end to end, building that path is work of this tree.
|
|
31
|
-
- The issue's primary document is the root spec: extend it in place, because a second "spec" artifact replaces the human's document. Mark each requirement of it as a `requirement` block, and write the issue's Verification document (`verification.md`) beside it: one `check` block per thing that must be proved, each with `serves` naming the requirements it serves, its `surface` (`browser`, `cli` or `api`), its `run`, its `when` (`before_merge`, `after_merge` or `default_branch`) and its `negative_control`. The checks are the gate every later role reads: the implementer and the tester record a result against each, and the reviewer approves only once every `before_merge` check of the issue has a passing result current at the head. The
|
|
33
|
+
- The issue's primary document is the root spec: extend it in place, because a second "spec" artifact replaces the human's document. Mark each requirement of it as a `requirement` block, and write the issue's Verification document (`verification.md`) beside it: one `check` block per thing that must be proved, each with `serves` naming the requirements it serves, its `surface` (`browser`, `cli` or `api`), its `run`, its `when` (`before_merge`, `after_merge` or `default_branch`) and its `negative_control`. The checks are the gate every later role reads: the implementer and the tester record a result against each, and the reviewer approves only once every `before_merge` check of the issue has a passing result current at the head. The decomposition's order and the plan's tasks go in the children and the planner's `plan` handoff, not the root spec.
|
|
32
34
|
- Read `skill://legion-architect/references/design-gate.md` before you write the root spec and before any edit to it after approval, because any new version closes the gate. It also names the design gate steps your `Legion addressing` line declares, the document each step records, and says that a sub-architect runs none.
|
|
33
35
|
- File every child of your decomposition before `dispatch request-approval`, and before `register_gate` where your line gates no step: the gate's opening starts your root's own first phase unless Dispatch shows an open child under it, so a child filed after it opens leaves the root working beside its children.
|
|
34
|
-
- A root architect then ends its turn and releases no
|
|
36
|
+
- A root architect then ends its turn and releases no child until every gated step is open: a delivered wake shows `design-approved` on the root, and under more than one step `gatedSteps` in your record (the `legion` tool's `read_record`) shows each step `approved`. The daemon starts no phase in the tree before that. A root that waited on a person's answer gets no `design-approved` notice: a resume task saying "this resume is your design-approved wake" is that wake. A sub-architect waits for none and releases its children with `release_children`.
|
|
35
37
|
|
|
36
38
|
## 2. Children in flight
|
|
37
39
|
|
|
38
|
-
- Set a child's dependencies in one call naming every issue it waits on: `dispatch issue-update --issue <child> --blocked-by <a> --blocked-by <b>`, or `--blocked-by` on `dispatch issue` at creation. The call replaces the child's whole list, so read its existing links first (`dispatch read`). Release it with the rest: `legion({ op: "release_children", issues: ["LEGION-41", "LEGION-42"] })`. Legion holds a released child that waits on an issue not `done` and starts it itself at its last blocker's close. A child that builds on another repository's merged change is not held this way: release it as `skill://legion-architect/references/decomposition.md` "Across repositories" says.
|
|
39
|
-
- Release every child as soon as it is filed,
|
|
40
|
+
- Set a child's dependencies in one call naming every issue it waits on: `dispatch issue-update --issue <child> --blocked-by <a> --blocked-by <b>`, or `--blocked-by` on `dispatch issue` at creation. The call replaces the child's whole list, so read its existing links first (`dispatch read`). Release it with the rest: `legion({ op: "release_children", issues: ["LEGION-41", "LEGION-42"] })`. Legion holds a released child that waits on an issue not `done` and starts it itself at its last blocker's close. A child that builds on another repository's merged change is not held this way: release it as `skill://legion-architect/references/decomposition.md` "Across repositories" says. No child of this tree waits on a sibling's merge in a repository this tree already has a pull request in: that work rides in the pull request that carries it (section 1).
|
|
41
|
+
- Release every child as soon as it is filed, each with `--blocked-by` the issues it waits on, and never hold a release back for a close: a released child, held or running, keeps the root's close waiting, but once the root's own pull request has merged the root closes the moment its last released child leaves, and a child released after that finds no live tree and runs as an orphan root of its own.
|
|
40
42
|
- On each `child-closed`, read the child's handoff. Edit the spec of each open child the result changes, and take out each child it makes unneeded with `park_child` and say why with `dispatch comment`.
|
|
41
43
|
- Before you park a child for good (one made unneeded, or parked so its tree can finish), set the `--blocked-by` list of each sibling that waits on it again without it (one call with the rest of its list, or `--clear-blocked-by` when it was the only one), or park that sibling too: a parked child is stopped, not done, and its dependents stay held with no notice. A park followed by `rerun_child` keeps its dependents' links: they wait through the rerun and start when the rerun's merge closes the child.
|
|
42
44
|
- A released child holds an admission slot of its own while a worker of it has work, and waits in the admission line for one, so a tree of four working issues holds four of the cap's issues. It never becomes a root while your tree is live.
|
|
43
45
|
|
|
44
46
|
## 3. Children complete
|
|
45
47
|
|
|
46
|
-
Your issue's own first phase does not start while Dispatch shows an open child under it, one you filed and have not released included: the daemon tells you once per issue and run of its tree with a `children-first` notice naming each open child and the Dispatch status that holds it, and starts the parent at its next admission slot once the last of them has left the workflow, closed, gone to `icebox`, or been moved out from under it, to another parent or to none. So file and release
|
|
48
|
+
Your issue's own first phase does not start while Dispatch shows an open child under it, one you filed and have not released included: the daemon tells you once per issue and run of its tree with a `children-first` notice naming each open child and the Dispatch status that holds it, and starts the parent at its next admission slot once the last of them has left the workflow, closed, gone to `icebox`, or been moved out from under it, to another parent or to none. So file and release every child at once, and park what should not run. No notice marks the last child's close. A child filed after your issue started runs beside it, and the daemon refuses the parent's `sign_off` while that child is in the workflow: run the parent's last round (section 4) so its head carries the child's merge.
|
|
47
49
|
|
|
48
50
|
## 4. Integration verification
|
|
49
51
|
|
|
@@ -64,7 +64,7 @@ parent's check: a child's pull request never makes a parent check's result curre
|
|
|
64
64
|
that carries part of a parent's requirement proves it with a check on the child's own
|
|
65
65
|
Verification document whose `serves` names that requirement. Nothing in the tree builds on the parent's own merge, because that
|
|
66
66
|
merge comes last: a child that waits for it waits forever, so work another child builds on is a
|
|
67
|
-
|
|
67
|
+
child of its own, in a repository of its own or with a deploy of its own. While the approved head does not carry those merges, or the result of
|
|
68
68
|
a check in another repository is older than the close of a child it needs, leave
|
|
69
69
|
the issue in `accepting` until each of those children has closed (`child-closed`) or been parked
|
|
70
70
|
for good (`child-status`). Move a parked child's criterion to other work first; never downgrade
|
|
@@ -13,7 +13,7 @@ and an earlier one with `--version <n>`. The child's Conversation lists every ve
|
|
|
13
13
|
|
|
14
14
|
## Adopting children
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Release every adopted child at once with
|
|
17
17
|
`legion({ op: "release_children", issues: ["LEGION-41", "LEGION-42"] })`. The daemon runs nothing on
|
|
18
18
|
a child until it is released. Once it is released, the daemon moves it to `todo` and, while the
|
|
19
19
|
root's design gate is open, starts its first phase itself at the child's own admission slot, and
|
|
@@ -44,18 +44,25 @@ EOF
|
|
|
44
44
|
that each child should not exist. Name every child that is a to-do, a fix found in review, a
|
|
45
45
|
proof of the root's acceptance, or a part of the architect's own work, and say where it folds:
|
|
46
46
|
the root's pull request or an open child's."* `wait` for its verdict, since it runs as a job of
|
|
47
|
-
its own. A child survives only when it
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
its own. A child survives it only when it needs a repository or a deploy order of its own: its
|
|
48
|
+
code is in a repository no open pull request of this tree is in, or it must be applied on its
|
|
49
|
+
own before other work of the tree can build on it. A repeated change — the same edit per
|
|
50
|
+
program, directory or instance of this tree's own work — is one child, never one per instance.
|
|
51
|
+
Work in a repository this tree already has a pull request in, which waits on a sibling's merge,
|
|
52
|
+
is no child and is not parked in `backlog`: write it into the spec of the issue whose pull
|
|
53
|
+
request carries it — the parent's, or the sibling's when the parent's pull request is in another
|
|
54
|
+
repository — so one pull request lands it in order, and a deletion of what this tree replaced
|
|
55
|
+
rides in the pull request of the last change it waits on the same way.
|
|
56
|
+
Post the verdict, with the folds you made, as one `dispatch comment` on your own issue (the
|
|
57
|
+
root, in a root session). Its first line is exactly `Red-team verdict for <root key>:`, with
|
|
58
|
+
the root's own key in place of the placeholder, so a reader can find it among the issue's
|
|
59
|
+
comments; the controller's weekly audit matches that line, and counts no child filed before
|
|
60
|
+
the release that first asked for it. The architect who wrote the list reads every child on it
|
|
61
|
+
as needed, and the comment stops a later session filing the same children again.
|
|
62
|
+
- A child that duplicates another issue and is not yet released is never released: fold it by the
|
|
63
|
+
duplicate rule of `skill://dispatch-first`. The daemon records a child at its release and
|
|
64
|
+
re-asserts a status only on an issue it has recorded, so that close stands. A released duplicate
|
|
65
|
+
goes out with `park_child` instead.
|
|
59
66
|
- Then paste each survivor's whole spec into a fresh `task(agent="plan-gap-analyst")` to check it
|
|
60
67
|
against `skill://dispatch-writing-specs`, `wait` for its result, and fix what it flags: your own
|
|
61
68
|
reread misses the terms
|
|
@@ -70,11 +77,13 @@ EOF
|
|
|
70
77
|
- File every child before you request the root spec's approval, and before `register_gate` under
|
|
71
78
|
`gates.design: off`: the gate's opening starts the root's own first phase unless Dispatch shows
|
|
72
79
|
an open child under it, so a child filed after that leaves the root working beside its children.
|
|
73
|
-
-
|
|
74
|
-
|
|
75
|
-
released child until its last blocker closes, and a child is inert only until it
|
|
76
|
-
|
|
77
|
-
|
|
80
|
+
- Give a child `--blocked-by` only for an issue outside this tree's own repositories, or one whose
|
|
81
|
+
deploy another child builds on, then release every child at once with `release_children`:
|
|
82
|
+
Legion holds a released child until its last blocker closes, and a child is inert only until it
|
|
83
|
+
is released. No child waits on a sibling's merge in a repository this tree already has a pull
|
|
84
|
+
request in; that work is written into the spec of the issue whose pull request carries it.
|
|
85
|
+
Each issue of the tree whose worker has work holds an admission slot of its own, so four
|
|
86
|
+
working children take four of the deployment's cap.
|
|
78
87
|
- Never release a child `--blocked-by` its own parent: the parent now merges last, so that child
|
|
79
88
|
would wait forever.
|
|
80
89
|
- Do not pass `--assignee`, because the default keeps the tree's questions in one Inbox: under the
|
|
@@ -174,7 +174,7 @@ tree is at work, the root returns its admission slot and waits on its design gat
|
|
|
174
174
|
approval or changes request delivers your next task, and so does a newer spec version once
|
|
175
175
|
Dispatch shows the request back on your turn. While the root waits on a question instead, the
|
|
176
176
|
human's approval or changes request of the spec answers that wait too, and the task says how the
|
|
177
|
-
spec stands. Release no
|
|
177
|
+
spec stands. Release no child until every gated step is open: a delivered wake shows
|
|
178
178
|
`design-approved` on the root, or a task says its design gate is open, and under more than one
|
|
179
179
|
step `gatedSteps` in your record shows each one `approved`. The daemon sends a root waiting on a
|
|
180
180
|
person no `design-approved` notice, and "this resume is your design-approved wake" is that wake.
|
|
@@ -211,13 +211,13 @@ to the version it writes. Once they have answered:
|
|
|
211
211
|
|
|
212
212
|
- When the answer is the whole change, request nothing: the document's chip reads Approved at the
|
|
213
213
|
new version, its reviews dialog shows the review in their name, and the `design-approved` the
|
|
214
|
-
daemon sent opens nothing new. Carry on, and release no
|
|
214
|
+
daemon sent opens nothing new. Carry on, and release no child because of it.
|
|
215
215
|
- When the answer changes the Summary, a requirement, a check or the tree's scope, rewrite that text and
|
|
216
216
|
repeat steps 3 to 5 on the new version, with a one-sentence `--summary` naming what changed since
|
|
217
217
|
the earlier approval, then the reviews' sentence (the card shows that approval, `Approved at
|
|
218
218
|
version N on <date> by <who>.`, and its `Show diff` control carries the detail). The rewrite
|
|
219
219
|
closes the gate until they approve;
|
|
220
|
-
work the daemon started at the answer keeps running. Release no
|
|
220
|
+
work the daemon started at the answer keeps running. Release no further child until the `design-approved`
|
|
221
221
|
after the rewrite; work already in flight continues. Every merger's `READY` in the tree is
|
|
222
222
|
refused until a human approves the latest version.
|
|
223
223
|
|
|
@@ -238,7 +238,7 @@ are unchanged,
|
|
|
238
238
|
the plan is the record and the tree carries on; otherwise change the root spec and request
|
|
239
239
|
approval again.
|
|
240
240
|
|
|
241
|
-
Later
|
|
241
|
+
Later children and re-scoping open children toward the same requirements need no spec edit and no new
|
|
242
242
|
approval. A child issue's spec is never gated, and the child's own Verification document is
|
|
243
243
|
written by its architect.
|
|
244
244
|
|
|
@@ -18,16 +18,16 @@ and 7 (a conflict, the hand-over, the close) is in
|
|
|
18
18
|
|
|
19
19
|
| Wake | Procedure |
|
|
20
20
|
| --- | --- |
|
|
21
|
-
| `carried-gate-opened` | Only a root whose catch-up said `carried` gets it. Its `version` and `reason`: a human approved the old root's spec document at `version`, which opened the gate this root carried; the daemon has released any READY it refused and started any child waiting on it. It names no step: the carry brings the old root's gate records, and the catch-up reports every gated step the carry did not bring as unregistered, so register each of those on this issue's own document for that step (`skill://legion-architect/references/design-gate.md`). It is not `design-approved`: do not proceed to section 2 or release a
|
|
21
|
+
| `carried-gate-opened` | Only a root whose catch-up said `carried` gets it. Its `version` and `reason`: a human approved the old root's spec document at `version`, which opened the gate this root carried; the daemon has released any READY it refused and started any child waiting on it. It names no step: the carry brings the old root's gate records, and the catch-up reports every gated step the carry did not bring as unregistered, so register each of those on this issue's own document for that step (`skill://legion-architect/references/design-gate.md`). It is not `design-approved`: do not proceed to section 2 or release a child because of it, and start nothing. |
|
|
22
22
|
| `child-status` | A child of your tree left the workflow or re-entered it, or joined your tree from the admission queue; the reason names the child and its new status, or reads `<KEY> joined the tree of <ROOT> from the admission queue`. `todo` (a human's move, your `release_children`, or your `rerun_child`) means it runs again under your tree from planning, and the daemon starts it; you start nothing. A child held for a blocker starts the same way once nothing holds it, and the reason, naming it and its root, says what cleared the hold: its last blocker's close, naming the issues it waited on, now done, or a person or you taking its blocked-by links off; you start nothing for it either. `backlog`, `icebox` or `triage` (a human's move, or your `park_child`) means the daemon has suspended its workers, and it advances no further until it is set back to `todo`. Joined from the admission queue means a root that waited for a slot and never ran, which a person made a child of your tree or your `release_children` adopted, now runs in your tree with no architect of its own and takes an admission slot of its own when its work starts, from planning when it is `todo` and parked otherwise; one that waits on an unfinished blocked-by issue is held for that blocker like any other child, and starts when nothing holds it; you start nothing. A reason saying an issue is beneath an ancestor whose reviewed head the daemon has already accepted is a person's `todo` the daemon recorded and holds at `admitted`: nothing starts it until that ancestor closes, and a `request_backward_move` of the ancestor is what runs it under the ancestor now. What the rest of the tree does is your decision. When a park leaves no child that an issue in `accepting` waits on, run that issue's last round (`skill://legion-architect/references/acceptance.md`). |
|
|
23
23
|
| `child-closed` | Read the child completion and remaining open children. Re-scope open work, park obsolete work for good with `park_child` (first take its link off each sibling that waits on it, or park that sibling too, as section 2 says), and file any new child the result calls for, after the red-team check in `skill://legion-architect/references/decomposition.md`, and release it at once with `release_children`, blocked by what it builds on (section 2). When a worker waits on this child for a missing surface (see `phase-finished`), tell it to continue with `envoy_publish` to its role topic. The last child's close is not the end-game (section 3). When it was the last child an issue in `accepting` waits on, run that issue's last round (`skill://legion-architect/references/acceptance.md`). |
|
|
24
|
-
| `child-release-stalled` | Its `reason` names a child your `release_children` released that your tree has not recorded, its parent, and why, one of three: the parent's release has not reached the daemon after three tries; or GitHub did not answer the App installation read of a repository on the child's parent chain, and the
|
|
24
|
+
| `child-release-stalled` | Its `reason` names a child your `release_children` released that your tree has not recorded, its parent, and why, one of three: the parent's release has not reached the daemon after three tries; or GitHub did not answer the App installation read of a repository on the child's parent chain, and the release left the child unrecorded because a parent on that chain is not `todo`, or did not write the child `todo` because its parent is not recorded; or the daemon's recovery of such a repository, once GitHub answered, still left children unrecorded (one notice per tree lists them all, each with its parent). Read the parent's record with the `legion` tool's `read_record` (`issue` the parent); release the parent first when the `reason` says to, then run `release_children` for the child again once the parent is recorded in your tree, and tell the controller if it never is. |
|
|
25
25
|
| `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. |
|
|
26
26
|
| `children-first` | An issue of your tree starts no phase of its own while Dispatch shows an open child under it, a child you filed and have not released included. The reason names the parent and each open child with the Dispatch status that holds it, and each cycle it found (a child `blocked_by` an issue above it on its own parent chain, which only a person taking that link off ends: open a `dispatch ask`). It comes once per issue and run of its tree. Release every child that should run, and close, `park_child` or re-parent the rest; the daemon starts the parent's first phase at its next admission slot once the last open child has left the workflow. You start nothing. |
|
|
27
|
-
| `design-approved` | Its `version` is the approved one, and it names no step: under more than one gated step, read `gatedSteps` in your own record (the `legion` tool's `read_record`) to see which step that version opened and whether another still holds the tree. A human approved the document of one gated step at its current version; that step is open. Proceed to section 2 once every gated step is `approved` — 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, "After approval" in `skill://legion-architect/references/design-gate.md`), opens nothing new: carry on, and release no
|
|
27
|
+
| `design-approved` | Its `version` is the approved one, and it names no step: under more than one gated step, read `gatedSteps` in your own record (the `legion` tool's `read_record`) to see which step that version opened and whether another still holds the tree. A human approved the document of one gated step at its current version; that step is open. Proceed to section 2 once every gated step is `approved` — 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, "After approval" in `skill://legion-architect/references/design-gate.md`), opens nothing new: carry on, and release no child because of it. A root that was waiting on a question in `awaiting_person` gets no such notice: the task that resumes it says "Your spec is approved at version N and the design gate is open: this resume is your design-approved wake", and that resume is this wake — proceed to section 2 on it. |
|
|
28
28
|
| `design-changes-requested` | Its `version` and `reason`, and no step: under more than one gated step, read `gatedSteps` in your own record (the `legion` tool's `read_record`) and take the step whose `status` is `changes_requested`. A human asked for changes to that step's document at `version`, for `reason`. Revise that document as the reason asks and request approval again as "The armed gate" in `skill://legion-architect/references/design-gate.md` says; end your turn; that step is closed. On a root that was waiting on a question in `awaiting_person`, a review the human gave after the wait began comes in the resume task, not in this notice, whether it answered the wait or came after something else did: the task names the review with its version and reason, unless a later approval superseded it. A `version` below the latest, or a second request for one version, is a request that reached the daemon late: read it as the same section's last paragraph says. Each request arrives once. |
|
|
29
29
|
| `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 finishing phase's handoff on the Dispatch issue (`dispatch handoff-read --issue <issue> --phase <word>`); 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 the full condition in "After approval" of `skill://legion-architect/references/design-gate.md`: when the approved Summary, the requirements, the checks, the scope and the settled decisions still hold, the plan is the record; otherwise change the root spec and request approval again as that section 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` (a parent only after its last round, "The last round" in `skill://legion-architect/references/acceptance.md`) or send it back with `request_backward_move`. A `tester` notice with `verdict: "fail"` — its handoff carries `implementerProof.verdict: "rejected"`, or a failure naming a check with no current passing result — has gone back to the **implementer** by the daemon's table; never record that check's result 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: build the surface in this tree: a child issue (infrastructure, tooling, or a skill) after the red-team check in `skill://legion-architect/references/decomposition.md`, or, when the red team folds it, tell the waiting worker to move the issue back to `implementing` with `request_backward_move`, naming the surface. When it is a child, tell the waiting worker to continue with `envoy_publish` to its role topic once that child's `child-closed` arrives. That report is never a reason to advance the phase. |
|
|
30
|
-
| `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: no handoff is a commit, so every such push counts except a push by the review App (a planner's, tester's, reviewer's or architect's), and the head pushed onto a red the tester's red tests earned (a planned red) does not count either. 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 or on a settled head whose base branch requires no check and no workflow, the tester's red tests 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, or onto a settled head whose base branch requires no check and no workflow, 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. 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. 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 rounds the cap is for. Each `review_round_cap` notice is a decision you make in the turn it wakes, never one left for the next round to make: read what every counted round found, choose one of the three below, act on it in the same turn, and post it as a `dispatch comment` on the issue naming the rounds you read and the choice. **Narrow**: the change itself is sound but each round finds more around it; tell the implementer and the reviewer (`envoy_publish` to each one's role topic) which checks of the issue the next round answers; a correctness finding is still fixed in this pull request, and so is a wording nit or any clear fix in a file it touches, so the `Fast-follow:` line carries only cleanup in files this pull request does not touch. **Split**: part of the work cannot settle in this pull request
|
|
30
|
+
| `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: no handoff is a commit, so every such push counts except a push by the review App (a planner's, tester's, reviewer's or architect's), and the head pushed onto a red the tester's red tests earned (a planned red) does not count either. 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 or on a settled head whose base branch requires no check and no workflow, the tester's red tests 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, or onto a settled head whose base branch requires no check and no workflow, 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. 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. 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 rounds the cap is for. Each `review_round_cap` notice is a decision you make in the turn it wakes, never one left for the next round to make: read what every counted round found, choose one of the three below, act on it in the same turn, and post it as a `dispatch comment` on the issue naming the rounds you read and the choice. **Narrow**: the change itself is sound but each round finds more around it; tell the implementer and the reviewer (`envoy_publish` to each one's role topic) which checks of the issue the next round answers; a correctness finding is still fixed in this pull request, and so is a wording nit or any clear fix in a file it touches, so the `Fast-follow:` line carries only cleanup in files this pull request does not touch. **Split**: part of the work cannot settle in this pull request. It is a child only when it needs a repository or a deploy order of its own (`skill://legion-architect`, section 1), and then it goes through the red-team check in `skill://legion-architect/references/decomposition.md` and is filed as a child of this issue's parent, never of this one, since this issue closes at its own merge. Work in a repository this tree already has a pull request in is no child and is not parked in `backlog`: write it into the spec of the issue whose pull request carries it, the parent's, or a sibling's when the parent's pull request is in another repository. On the root, narrow it to what settles here and open a `dispatch ask` for the rest (`skill://legion-worker/references/findings.md`): a person decides whether that becomes an issue. Tell the implementer and the reviewer that this pull request no longer carries it. **Accept the rounds**: each round found less than the one before it, and the last found no defect in the change itself (a stale line, a comment that states an old default); let the loop run and say why in the comment. A later notice is a new decision: accept again only when its round found less than the round you last accepted, and otherwise narrow or split. A finding that changes what the spec requires or what a check proves is a decision block in the root spec ("After approval" in `skill://legion-architect/references/design-gate.md`), and `dispatch ask` is only for a decision that needs a human's authority; neither replaces the choice. Judge a worker's backward move by the reason that worker gives. A move you ordered for a conflict found before `awaiting_merge` ("A conflicting head" in `skill://legion-architect/references/acceptance.md`) 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. |
|
|
31
31
|
| `pr-merged` | The pull request merged and the issue did not close; its `reason` says why. Merged before the issue reached `awaiting_merge`: the workflow runs on, and the merge closes the issue the moment it gets there, your acceptance and retro first. Merged while the rest of the tree still runs: the reason names the pull request and every issue the close waits for, and the root waits in `awaiting_merge`, its workers kept and live and no admission slot held, until the last of them is done or parked ("The close" in `skill://legion-architect/references/acceptance.md`). Either way you start nothing and no further notice follows. |
|
|
32
32
|
| `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. |
|
|
33
33
|
| `pull-request-missing` | Its `reason` names the issue, the repository, the branch and the handoff commit: the issue's implementer completed at that commit and GitHub lists no open pull request on the branch, so the issue stays in `implementing`. Tell the implementer (`envoy_publish` to its role topic) to open the pull request from that branch; the daemon records it and moves the issue to `testing` once it is open. When the work should not go on, decide as the `pr-closed-unmerged` row says. It comes once per handoff commit. |
|
|
@@ -44,4 +44,4 @@ and 7 (a conflict, the hand-over, the close) is in
|
|
|
44
44
|
## A root that joins your tree, and a refused release
|
|
45
45
|
|
|
46
46
|
- A root that waited in the admission queue, never ran and never left another tree joins your tree once a person makes it your child, if it is `todo`, and otherwise at your release of it (the `child-status` row of [wake routing](skill://legion-architect/references/wake-routing.md)); once in, it is held for its blockers like any child you release. A root that left another tree, handed over by its finished tree or admitted as an orphan, stays its own tree and never joins yours, even one that was your root's child before your root was admitted again. A labelled `todo` issue under a child you have not released, unless it ran as a tree of its own or left another tree, is held under that unreleased child (not a blocked-by hold): it waits for no slot until your release of it or of that child brings it in, with any `todo` child of its own.
|
|
47
|
-
- A release that cannot take a child in is refused 409 and writes nothing, its message naming the child and why: release the rest
|
|
47
|
+
- A release that cannot take a child in is refused 409 and writes nothing, its message naming the child and why: release the rest again without it. `CHILD_NOT_ADOPTABLE` is an issue that runs or ran as a tree of its own, carries a run handed over to it, or was handed over (or admitted as an orphan) as a root of its own: leave it there, or ask the controller. `CHILD_IN_OTHER_TREE` is another tree's child. When the tree it names is a root held under an unreleased child of yours (a root that queued and never ran, so it has no architect of its own), release that root, or the unreleased child it sits under, instead: its children come with it. Otherwise re-parent the child back in Dispatch, or ask that tree's architect or the controller.
|
|
@@ -33,10 +33,13 @@ Launch, `gh`, pull requests, takeover: `skill://legion-controller/references/ses
|
|
|
33
33
|
|
|
34
34
|
1. Recheck [trees waiting on a root claim](#trees-waiting-on-a-root-claim).
|
|
35
35
|
2. Do the wake's work, or, on a start message, the start procedure below.
|
|
36
|
-
3. On your first turn of each UTC
|
|
36
|
+
3. On your first turn of each UTC week, whatever woke you: run the weekly audit
|
|
37
|
+
(`skill://legion-controller/references/weekly-audit.md`) before the day's report, so its
|
|
38
|
+
numbers and its nudges go in that report.
|
|
39
|
+
4. On your first turn of each UTC day, whatever woke you: run the weekly-plan reconciliation
|
|
37
40
|
(`skill://legion-controller/references/backlog-stewardship.md`), then post the
|
|
38
41
|
[daily report](#daily-report).
|
|
39
|
-
|
|
42
|
+
5. Name the [free slots](#free-slots) in your summary, so a person sees room to hand Legion work.
|
|
40
43
|
|
|
41
44
|
## What happened before you started
|
|
42
45
|
|
|
@@ -73,6 +73,13 @@ every issue by its `dispatch://` link and what it does for the reader, every pul
|
|
|
73
73
|
name, its `detail`, and a deployment row's `configLine`. Say who acts on it, as
|
|
74
74
|
`skill://legion-controller/references/wakes.md` says for `tick`. With none open, say nothing of
|
|
75
75
|
them.
|
|
76
|
+
- **The week** (on the report of your first turn of each UTC week only, from
|
|
77
|
+
`skill://legion-controller/references/weekly-audit.md`): the window's two times; the forced
|
|
78
|
+
creates by filer with the no-answer creates apart; the tree sizes in the five buckets with the
|
|
79
|
+
parked descendants; each nudge you sent, by its tree; and the duplicate audit's pairs, those
|
|
80
|
+
you folded and those you skipped with the event that settles each. Name beside each number the
|
|
81
|
+
command that recounts it. When you lead a stream that keeps a scoreboard, the same two measures
|
|
82
|
+
go on it under `forced_creates_by_filer` and `tree_sizes`.
|
|
76
83
|
|
|
77
84
|
A day with nothing finished says so in one sentence. When the lists do not fit, give each list's
|
|
78
85
|
count and name its issues of `--priority 0` and `--priority 1`, in board rank order.
|