@sjawhar/pi-legion 11.0.0 → 11.1.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.
Files changed (28) hide show
  1. package/README.md +5 -4
  2. package/agents/plan-gap-analyst.md +24 -16
  3. package/agents/plan-reviewer.md +59 -16
  4. package/dist/legion.js +17 -16
  5. package/dist/skills/leading-a-work-stream/SKILL.md +96 -0
  6. package/dist/skills/leading-a-work-stream/references/daily-plan.md +8 -0
  7. package/dist/skills/leading-a-work-stream/references/decision-rights.md +6 -0
  8. package/dist/skills/leading-a-work-stream/references/direction-and-coaching.md +13 -0
  9. package/dist/skills/leading-a-work-stream/references/dispatch-upkeep.md +7 -0
  10. package/dist/skills/leading-a-work-stream/references/sources.md +9 -0
  11. package/dist/skills/leading-a-work-stream/references/speed-tick.md +44 -0
  12. package/dist/skills/leading-a-work-stream/references/user-quotes.md +7 -0
  13. package/dist/skills/legion-architect/SKILL.md +4 -2
  14. package/dist/skills/legion-architect/references/acceptance.md +11 -3
  15. package/dist/skills/legion-architect/references/design-gate.md +32 -17
  16. package/dist/skills/legion-architect/references/wake-routing.md +2 -2
  17. package/dist/skills/legion-controller/SKILL.md +27 -16
  18. package/dist/skills/legion-controller/references/backlog-stewardship.md +17 -2
  19. package/dist/skills/legion-controller/references/daily-report.md +4 -4
  20. package/dist/skills/legion-controller/references/finding-asks.md +111 -14
  21. package/dist/skills/legion-controller/references/slack-mentions.md +2 -2
  22. package/dist/skills/legion-controller/references/triage.md +9 -5
  23. package/dist/skills/legion-worker/SKILL.md +5 -4
  24. package/dist/skills/legion-worker/references/conflicts-and-rewrites.md +23 -16
  25. package/dist/skills/legion-worker/references/findings.md +126 -31
  26. package/dist/skills/legion-worker/references/merge-gate.md +48 -17
  27. package/dist/skills/legion-worker/references/pr-body.md +9 -5
  28. package/package.json +1 -1
package/README.md CHANGED
@@ -37,16 +37,17 @@ controller, installs both the same way.
37
37
 
38
38
  Released installs come from npm as `@sjawhar/pi-legion`. The tarball ships `dist/legion.js`, which
39
39
  bundles every dependency except the Oh My Pi host packages (`@legion/pi-shared`, `@legion/contracts`
40
- and `@legion/envoy-client` are inlined), `dist/THIRD_PARTY_NOTICES`, the eight Legion skills staged at
40
+ and `@legion/envoy-client` are inlined), `dist/THIRD_PARTY_NOTICES`, the nine Legion skills staged at
41
41
  `dist/skills` (`legion-architect`, `legion-controller`, `legion-oracle`, `legion-retro`,
42
- `legion-worker`, `ce-simplify-code`, `thermonuclear-code-quality`, `thermonuclear-deep-review`;
42
+ `legion-worker`, `leading-a-work-stream`, `ce-simplify-code`, `thermonuclear-code-quality`, `thermonuclear-deep-review`;
43
43
  the manifest's `omp.skills: ["dist/skills"]` is what lets Oh My Pi discover them), and `agents/`,
44
44
  the task agents Legion's prompts dispatch (`oracle`; the reviewer's pair
45
45
  `thermonuclear-deep-review` and `thermonuclear-code-quality`; `deep-worker`; the planner's
46
- `plan-gap-analyst`, which also reads a spec before a human does, and `plan-reviewer`), each
46
+ `plan-gap-analyst` and `plan-reviewer`, which also review a root spec before every approval
47
+ request, `plan-reviewer` checking each of its factual claims), each
47
48
  declaring its model as a role the operator maps (`docs/kubernetes.md`, "Model roles"). The Dispatch
48
49
  and Envoy skills (`asd-ste100`, `dispatch`, `dispatch-first`,
49
- `dispatch-brainstorming`, `envoy`) ship in `@sjawhar/pi-envoy`; a link from a Legion skill into one
50
+ `dispatch-writing-specs`, `envoy`) ship in `@sjawhar/pi-envoy`; a link from a Legion skill into one
50
51
  of them is written `skill://dispatch/...`, which resolves by name once both are installed.
51
52
 
52
53
  `scripts/pi-plugin-prepack.sh` at the repository root is both plugins' `prepack`: it builds the one
@@ -1,15 +1,19 @@
1
1
  ---
2
2
  name: plan-gap-analyst
3
3
  description: |
4
- Pre-planning and spec gap analyst. Read-only. Use before drafting a plan, or before a spec reaches
5
- a human: finds the hidden requirements, ambiguities, missing machine-checkable acceptance criteria,
6
- and unsupported claims an issue or spec carries, each with what its author must answer.
4
+ Pre-planning and spec gap analyst. It changes nothing. Use before drafting a plan, or before a
5
+ spec reaches a human: finds the hidden requirements, ambiguities, missing machine-checkable
6
+ acceptance criteria, and unsupported claims an issue or spec carries, each with what its author
7
+ must answer.
7
8
  # @oracle is the deployment's `oracle` model role; the Go daemon's boot gate refuses to start unless the operator's settings give
8
9
  # this agent a model, through modelRoles.oracle or a task.agentModelOverrides entry for it (docs/kubernetes.md, Operator configuration).
9
10
  model: ["@oracle"]
10
- tools: read, glob, grep, todo
11
- # The planner waits for the analysis before it drafts; without this, Oh My Pi runs a task agent in
12
- # the background whenever async jobs are enabled.
11
+ # bash runs read-only commands, so the agent checks a claim about live state itself.
12
+ # Oh My Pi has no per-agent command limit (bash.patterns is one setting for the whole session), so
13
+ # the Constraints below hold the agent to commands that read, as a rule holds an engineer.
14
+ tools: read, glob, grep, bash, todo
15
+ # The planner and the architect wait for the analysis before they draft or request approval.
16
+ # Without this, Oh My Pi runs a task agent in the background whenever async jobs are on.
13
17
  blocking: true
14
18
  color: yellow
15
19
  ---
@@ -28,11 +32,12 @@ owns the document.
28
32
  3. **Acceptance a machine cannot check.** A criterion with no command, test, or request whose
29
33
  expected output decides it, or a requirement with no criterion at all.
30
34
  4. **Claims and requirements with no source.** A statement about how a system works today that the
31
- code, its documentation or command output you were given does not show; and, when the caller
32
- gave you the owner's words to check the document against, a requirement that traces to neither
33
- them nor a cited fact. A requirement in a version a human approved has its source. Say what you
34
- checked. You cannot run commands, so a claim about live state you cannot read is reported as
35
- unverified, not as false.
35
+ code, its documentation or a read-only command does not show. Also, when the caller gave you the
36
+ owner's words to check the document against, a requirement that traces to neither them nor a
37
+ cited fact. A requirement in a version a human approved has its source. Say what you checked:
38
+ the file and line, or the command and its output. Check a claim about live state yourself with
39
+ a read-only command (`kubectl get`, `gh pr view`, `dispatch read`). Report a claim as unverified
40
+ only when nothing you can read or run decides it, and name the command that would.
36
41
 
37
42
  Read the code and the repository's own documentation before you call something a gap. A gap the
38
43
  issue, the code, or the documentation already answers is not a finding.
@@ -51,9 +56,12 @@ are not findings. When nothing changes the work, say so in one line.
51
56
 
52
57
  ## Constraints
53
58
 
54
- - **Read-only: you analyse; you do not implement.** Your file tools are read-only; do not ask for
55
- others or route edits through other means. Your session also carries the Dispatch and Envoy
56
- tools: read with them if you need to, but write nothing through them — no issue, comment, ask,
57
- message, suggestion or document edit, and nothing sent or published.
58
- - Cite what you read. Anything you did not read is an assumption and is written as one.
59
+ - **You change nothing. You analyse, and you do not implement.** Your file tools only read. Run
60
+ only commands that read. Never run a command that writes, removes, pushes, posts, merges, applies,
61
+ starts or stops anything, in a file, a repository, a cluster or a service. Run jj with
62
+ `--ignore-working-copy`, because every other jj command records the working copy. When you are
63
+ not sure that a command only reads, do not run it.
64
+ - Your session also carries the Dispatch and Envoy tools: read with them if you need to, but write
65
+ nothing through them — no issue, comment, ask, message, suggestion or document edit, and nothing
66
+ sent or published.
59
67
  - Do not write the plan or edit the document.
@@ -1,24 +1,33 @@
1
1
  ---
2
2
  name: plan-reviewer
3
3
  description: |
4
- Plan executability reviewer. Read-only. Use after drafting a plan: checks that it can be carried
5
- out as written, approves when in doubt, and names at most three blocking issues, each with its
6
- evidence.
4
+ Plan and spec reviewer. It changes nothing. Use after drafting a plan: checks that it can be
5
+ carried out as written, approves when in doubt, and names at most three blocking issues, each
6
+ with its evidence. Use before every approval request of a spec: checks each factual claim in it
7
+ against the code, the docs or a read-only command it runs, and marks each claim verified, false
8
+ or unverified.
7
9
  # @review is the deployment's `review` model role; the Go daemon's boot gate refuses to start unless the operator's settings give
8
10
  # this agent a model, through modelRoles.review or a task.agentModelOverrides entry for it (docs/kubernetes.md, Operator configuration).
9
11
  model: ["@review"]
10
- tools: read, glob, grep, todo
11
- # The planner waits for each verdict before it revises or proceeds; without this, Oh My Pi runs a
12
- # task agent in the background whenever async jobs are enabled.
12
+ # bash runs read-only commands, so the agent checks a claim about live state itself.
13
+ # Oh My Pi has no per-agent command limit (bash.patterns is one setting for the whole session), so
14
+ # the Constraints below hold the agent to commands that read, as a rule holds an engineer.
15
+ tools: read, glob, grep, bash, todo
16
+ # The planner and the architect wait for each verdict before they revise or proceed. Without this,
17
+ # Oh My Pi runs a task agent in the background whenever async jobs are on.
13
18
  blocking: true
14
19
  color: magenta
15
20
  ---
16
21
 
22
+ You review a plan or a spec. The caller says which, and a document it does not name is a plan. You
23
+ advise its author, and the author owns the document.
24
+
25
+ ## A plan
26
+
17
27
  You answer one question: can a capable engineer who has only this plan and the repository carry
18
- it out without getting stuck? You find blockers, not improvements. You advise the planner; the
19
- planner owns the plan.
28
+ it out without getting stuck? You find blockers, not improvements.
20
29
 
21
- ## What you check
30
+ ### What you check
22
31
 
23
32
  1. **References.** The files, functions, commands, and skills the plan names exist and hold what
24
33
  the plan says. Read them; do not assume.
@@ -32,7 +41,7 @@ planner owns the plan.
32
41
  You do not judge whether the approach is the best one, style, naming, edge cases the engineer can
33
42
  settle during the work, or anything you would simply do differently.
34
43
 
35
- ## Verdict
44
+ ### Verdict
36
45
 
37
46
  Approve when in doubt. A plan that is mostly clear is good enough. Reject only for a blocker: a
38
47
  reference that does not exist or does not hold what the plan says (read to confirm), a task with
@@ -47,15 +56,49 @@ A rejection names at most three blocking issues, the most severe first. Each giv
47
56
  A revised plan is read again in full and judged fresh: an issue the revision resolved is not
48
57
  raised again, and a second round is no place for requests that would not have blocked the first.
49
58
 
50
- ## Output
59
+ ### Output
51
60
 
52
61
  The first line is the verdict alone: `approved` or `rejected`. Then one or two sentences on why.
53
62
  On `rejected`, the numbered blocking issues. Nothing else.
54
63
 
64
+ ## A spec
65
+
66
+ A human approves the spec. Each false claim in it costs the human a second review. You check every
67
+ factual claim in it, one at a time, and you do not judge the design.
68
+
69
+ ### What you check
70
+
71
+ 1. **Each factual claim.** A claim states how something is today: what code does, what a file,
72
+ document, API or system holds or returns, a count, a version or a name. Check each claim
73
+ against its source. Read the file, or run a read-only command. Check a claim about live state
74
+ (a cluster, a pull request, an issue, an endpoint) yourself, for example with `kubectl get`,
75
+ `gh pr view`, `gh api` with no `-X`, `-f` or `-F` flag, `curl` with no `-X` or `-d` flag, or
76
+ `dispatch read`. A requirement, a decision or a proposal is not a claim.
77
+ 2. **The standard practice.** The spec names how this problem is normally solved and links its
78
+ sources, or it says why no standard practice applies. When the design departs sharply from that
79
+ practice, the spec says why.
80
+
81
+ ### Output
82
+
83
+ The first line is the verdict alone: `approved` when you verified every claim and the spec passes
84
+ check 2, `rejected` otherwise. Then one row for each claim, in the spec's order:
85
+
86
+ - **The claim**, quoted.
87
+ - **Verified**, **false** or **unverified**.
88
+ - **The evidence**: the file and line you read, or the command you ran and the part of its output
89
+ that decides the claim. For a false claim, what the source says instead.
90
+
91
+ Mark a claim unverified only when no read and no read-only command you can run decides it, and
92
+ name the command that would. Then one line on check 2. Nothing else.
93
+
55
94
  ## Constraints
56
95
 
57
- - **Read-only: you review; you do not implement.** Your file tools are read-only; do not ask for
58
- others or route edits through other means. Your session also carries the Dispatch and Envoy
59
- tools: read with them if you need to, but write nothing through them — no issue, comment, ask,
60
- message, suggestion or document edit, and nothing sent or published.
61
- - Do not rewrite the plan.
96
+ - **You change nothing. You review, and you do not implement.** Your file tools only read. Run only
97
+ commands that read. Never run a command that writes, removes, pushes, posts, merges, applies,
98
+ starts or stops anything, in a file, a repository, a cluster or a service. Run jj with
99
+ `--ignore-working-copy`, because every other jj command records the working copy. When you are
100
+ not sure that a command only reads, do not run it: mark the claim unverified and name the command.
101
+ - Your session also carries the Dispatch and Envoy tools: read with them if you need to, but write
102
+ nothing through them — no issue, comment, ask, message, suggestion or document edit, and nothing
103
+ sent or published.
104
+ - Do not rewrite the plan or the spec.
package/dist/legion.js CHANGED
@@ -14179,6 +14179,7 @@ var askEventPayloadFields = {
14179
14179
  text: exports_external.string().nullish(),
14180
14180
  at: exports_external.string().optional()
14181
14181
  }).nullish(),
14182
+ restored: exports_external.boolean().optional(),
14182
14183
  anchor: exports_external.object({
14183
14184
  quote: exports_external.string().optional(),
14184
14185
  mark_id: exports_external.string().optional(),
@@ -14433,7 +14434,7 @@ function componentsArgument(z2) {
14433
14434
  reason: z2.string({ min: 1 }).describe("For mode none: why this issue is not architectural (process, hiring, ops).").optional()
14434
14435
  }).describe("Attach the issue to architecture components. Attach the root before decomposing it; children inherit unless they choose.");
14435
14436
  }
14436
- var SPEC_WRITING_POINTER = 'Write a spec as the "Writing a spec" section of skill://dispatch says.';
14437
+ var SPEC_WRITING_POINTER = "Write a spec as skill://dispatch-writing-specs says.";
14437
14438
  function imagesArgument(z2, owner, cap) {
14438
14439
  return z2.array(z2.string({ min: 1 }), { min: 1 }).describe(`Optional local picture files to show inline: PNG, JPEG, GIF or WebP (by their bytes, not their name), each at most 25 MiB. Each is uploaded to ${owner} and appended after a blank line as one ![<file name>](dispatch://\u2026@vN) line, in this order; those lines count toward ${cap}.`).optional();
14439
14440
  }
@@ -14471,7 +14472,7 @@ var dispatchToolSpecs = [
14471
14472
  {
14472
14473
  name: "dispatch_issue",
14473
14474
  example: { project: "DSP", title: "Native workspace" },
14474
- description: "Create a native Dispatch issue for newly tracked work. Search first with dispatch search; if potentially duplicate issues exist, this returns 409 POSSIBLE_DUPLICATE unless --force is set after reading them. " + "When passing a spec, before calling, have a fresh read-only reader check it against the code and the human's words, then resolve every finding; a Legion architect uses `plan-gap-analyst`. " + "A spec holding an ask block whose body breaks its content rule (one or more question paragraphs, then at most one bullet list of options, last) is refused with 400 INVALID_ASK_BLOCK. " + `Do not use it when an existing issue already covers the work; read or update that issue instead. ${ISSUE_REFERENCE}`,
14475
+ description: "Create a native Dispatch issue for newly tracked work. Search first with dispatch search; if potentially duplicate issues exist, this returns 409 POSSIBLE_DUPLICATE unless --force is set after reading them. " + "When passing a spec, before calling, have a fresh read-only reader check it against the code and the human's words, then resolve every finding; a Legion architect uses `plan-gap-analyst`. " + "A spec holding an ask block whose body breaks its content rule (one or more question paragraphs, then at most one bullet list of options, last) is refused with 400 INVALID_ASK_BLOCK. " + "Create a project only when a person asks for one; work that spans projects goes in the project that owns most of it. " + `Do not use it when an existing issue already covers the work; read or update that issue instead. ${ISSUE_REFERENCE}`,
14475
14476
  arguments: (z2) => ({
14476
14477
  project: z2.string().describe("Project key for the new issue."),
14477
14478
  title: z2.string({ max: ISSUE_TITLE_MAX }).describe("Concise issue title, at most 1,000 characters."),
@@ -14482,7 +14483,7 @@ var dispatchToolSpecs = [
14482
14483
  spec: z2.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_POINTER}`).optional(),
14483
14484
  labels: z2.array(z2.string({ min: 1, max: 40 }), { max: 20 }).describe("Optional initial labels, at most 20 labels of up to 40 characters.").optional(),
14484
14485
  priority: z2.number({ int: true, min: 0, max: 3 }).describe("Optional coarse priority, a number from 0 to 3: 0 is highest and 3 is lowest.").optional(),
14485
- assignee: z2.string().describe("Email of the person who answers this issue's asks (someone who has signed in to Dispatch); defaults to your owner when you act for a person, else the parent's assignee, else the project's default assignee when it still names a person who has signed in, else unassigned.").optional(),
14486
+ assignee: z2.string().describe("Email of the person who answers this issue's asks (someone who has signed in to Dispatch); defaults to your owner when you act for a person, else the parent's assignee, else the project's default assignee when it still names a person who has signed in, else unassigned. Set it only when a person said who owns the issue.").optional(),
14486
14487
  components: componentsArgument(z2).optional(),
14487
14488
  repo: z2.string().describe("The repository the issue targets, owner/name, mapped to the issue's project in Settings; a child inherits its parent's when unset, and an issue with none on its parent chain takes the project's default.").optional()
14488
14489
  })
@@ -14494,7 +14495,7 @@ var dispatchToolSpecs = [
14494
14495
  priority: 1,
14495
14496
  before: "DSP-2"
14496
14497
  },
14497
- description: "Update an existing issue: move its lifecycle status, retitle it, replace its labels, set " + "its priority, link a URL (the pull request that delivers it, a run, a document), set its " + "route, set or clear its parent, replace the issues it is blocked by, attach it to " + "architecture components, set the repository it targets, or move it on its project's " + "board. Status is one of " + `${ISSUE_STATUSES.join(", ")}; outside Legion, move it yourself as the work advances; inside ` + "Legion the daemon moves it. A non-empty external_links is " + "merged into the issue's existing links by URL, so linking the pull request you just opened " + "keeps every earlier link; an empty external_links removes every link instead. components " + "replaces the issue's own attachment. Closing an issue " + "(status done) requires reason, the note that says why: it is posted on the issue as a " + "message, then the issue closes, because a closed issue refuses messages, comments, and " + "artifacts; reason goes only with status done. A closed issue takes only a board move " + "(before, after), components, " + "a reopening status (any status but done), and an external_links write that adds no new " + "link (so a link can move to the issue that replaced it); everything else, priority " + "included, waits for the reopen. " + "priority is yours to set and a human overrides it. before and after move the issue on " + "its project's board: before puts it above that issue, after below, and both together " + "land it between them. A project's board order is one agent's to keep; in a Legion " + "deployment that is the controller. At least one " + `field besides issue is required. ${ISSUE_REFERENCE}`,
14498
+ description: "Update an existing issue: move its lifecycle status, retitle it, replace its labels, set " + "its priority, link a URL (the pull request that delivers it, a run, a document), set its " + "route, set or clear its parent, replace the issues it is blocked by, attach it to " + "architecture components, set the repository it targets, or move it on its project's " + "board. Status is one of " + `${ISSUE_STATUSES.join(", ")}; outside Legion, move it yourself as the work advances; inside ` + "Legion the daemon moves it. A non-empty external_links is " + "merged into the issue's existing links by URL, so linking the pull request you just opened " + "keeps every earlier link; an empty external_links removes every link instead. components " + "replaces the issue's own attachment. Closing an issue " + "(status done) requires reason, the note that says why: it is posted on the issue as a " + "message, then the issue closes, because a closed issue refuses messages, comments, and " + "artifacts; reason goes only with status done. A closed issue takes only a board move " + "(before, after), components, " + "a reopening status (any status but done), and an external_links write that adds no new " + "link (so a link can move to the issue that replaced it); everything else, priority " + "included, waits for the reopen. " + "priority is yours to set and a human overrides it. before and after move the issue on " + "its project's board: before puts it above that issue, after below, and both together " + "land it between them. A project's board order is one agent's to keep; in a Legion " + "deployment that is the controller. Never reassign an issue a person holds to get a " + "faster answer: who holds it is their call. At least one " + `field besides issue is required. ${ISSUE_REFERENCE}`,
14498
14499
  arguments: (z2) => ({
14499
14500
  issue: z2.string().describe(ISSUE_REFERENCE),
14500
14501
  status: z2.enum(ISSUE_STATUSES).describe("New lifecycle status.").optional(),
@@ -14544,7 +14545,7 @@ var dispatchToolSpecs = [
14544
14545
  }
14545
14546
  ]
14546
14547
  },
14547
- description: "Open a to-do or permission only a human can give, or a decision that has no document to " + "live in. A question about the design an issue's document records is not this tool: write " + "it into that document as a decision block (dispatch doc-edit inserting an ask block at the " + "end of the section it concerns), at every phase, approved spec or not; the block reaches " + "the Inbox and its answer lands next to its context. Never give an ask an Approve option: a " + "document is approved through dispatch request-approval. Do not use this tool for a status " + "update or discussion; use dispatch message instead. " + ASK_QUESTION_CONTRACT + " " + ASK_OPTIONS_CONTRACT + " For an action only a human can perform, state what it changes and risks as constraints. " + "Anything that requires a human to do, including a credential or grant renewal, is an ask, " + "never a message. " + "Anchor a to-do about a document passage, thread reply_to/reply_to_ask, or cite a dispatch:// " + `reference \u2014 it must be answerable from its own text and anchor alone, never "see above". ` + `A quote anchor is pinned to its block. Question is at most ${ASK_QUESTION_MAX} characters, ` + `the lines images appends included, and has at most 8 options. ${OWNER_REFERENCE}`,
14548
+ description: "Open a to-do or permission only a human can give, or a decision that has no document to " + "live in. A question about the design an issue's document records is not this tool: write " + "it into that document as a decision block (dispatch doc-edit inserting an ask block at the " + "end of the section it concerns), at every phase, approved spec or not; the block reaches " + "the Inbox and its answer lands next to its context. Never give an ask an Approve option: a " + "document is approved through dispatch request-approval. Do not use this tool for a status " + "update or discussion; use dispatch message instead. Never create an issue to park a " + "question: the question is an ask on the issue it concerns. " + ASK_QUESTION_CONTRACT + " " + ASK_OPTIONS_CONTRACT + " For an action only a human can perform, state what it changes and risks as constraints. " + "Anything that requires a human to do, including a credential or grant renewal, is an ask, " + "never a message. Measure what you can first, and put the measurement and the size of the " + "population it covers in the question; drop an option that cannot fix most of that " + "population, never offer it cut down, and keep an option not to act (Hold). Set urgency high " + "when your work halts on the answer, and keep working on everything else. " + "Anchor a to-do about a document passage, thread reply_to/reply_to_ask, or cite a dispatch:// " + `reference \u2014 it must be answerable from its own text and anchor alone, never "see above". ` + `A quote anchor is pinned to its block. Question is at most ${ASK_QUESTION_MAX} characters, ` + `the lines images appends included, and has at most 8 options. ${OWNER_REFERENCE}`,
14548
14549
  arguments: (z2) => ({
14549
14550
  issue: z2.string().describe(ISSUE_REFERENCE).optional(),
14550
14551
  project: z2.string().describe("Project key owning the document.").optional(),
@@ -14608,7 +14609,7 @@ var dispatchToolSpecs = [
14608
14609
  kind: "retracted",
14609
14610
  reason: "A newer question supersedes this one."
14610
14611
  },
14611
- description: "Retract an open question that is moot or resolve one after finding the answer. This closes " + "the question without answering it. An ask that lives as an `ask` block in a document is " + 'closed in the document too. A reason beginning "removed from the document in version" is ' + "refused: that marks a retraction the document's own settlement wrote.",
14612
+ description: "Retract an open question that is moot or resolve one after finding the answer. This closes " + "the question without answering it. An ask that lives as an `ask` block in a document is " + 'closed in the document too. A reason beginning "removed from the document in version" is ' + "refused: that marks a retraction the document's own settlement wrote. Use retracted for an ask " + "a newer one replaces or the design made moot, resolved for one whose answer you found; an " + "answered ask cannot be resolved, and resolving records no decision by a person.",
14612
14613
  arguments: (z2) => ({
14613
14614
  ask: z2.string().describe("Ask id (uuid) to close; an 8+ hex prefix unique among this session's own open asks " + "works too."),
14614
14615
  kind: z2.enum(["retracted", "resolved"]).describe("Whether the ask is retracted or self-resolved."),
@@ -14637,7 +14638,7 @@ var dispatchToolSpecs = [
14637
14638
  {
14638
14639
  name: "dispatch_comment",
14639
14640
  example: { issue: "DSP-1", body: "Looks good." },
14640
- description: "Add review feedback to an issue or project document quote, or reply to a question asked with dispatch ask. " + "Do not use it for an exact replacement; use " + `dispatch suggest instead. A quote anchor is pinned to its block. Body is at most ${DISPATCH_BODY_MAX} characters, the lines images appends included. ${OWNER_REFERENCE}`,
14641
+ description: "Add review feedback to an issue or project document quote, or reply to a question asked with dispatch ask. " + "Do not use it for an exact replacement; use " + `dispatch suggest instead. A quote anchor is pinned to its block. A reply under an open ask asks for clarification, not an answer: answer it with reply_to_ask, or reword the ask with dispatch edit-ask, rather than opening a second ask. Body is at most ${DISPATCH_BODY_MAX} characters, the lines images appends included. ${OWNER_REFERENCE}`,
14641
14642
  arguments: (z2) => ({
14642
14643
  issue: z2.string().describe(ISSUE_REFERENCE).optional(),
14643
14644
  project: z2.string().describe("Project key owning the document.").optional(),
@@ -14677,7 +14678,7 @@ var dispatchToolSpecs = [
14677
14678
  {
14678
14679
  name: "dispatch_message",
14679
14680
  example: { issue: "DSP-1", body: "Implementation started." },
14680
- description: "Post a note humans must read now: a reply to a human's message or a deliverable that landed. A to-do only a human can " + "complete is an ask (dispatch ask), so it reaches their inbox. Never progress or status updates - Dispatch is a high-signal " + "record, not a log. Not a design decision (write it as a decision block in the document) or document feedback (dispatch comment). " + "To answer a human's direct message to this session - one sent from the Agents page, which names no issue - pass that message's bare id as " + "in_reply_to and no issue; the reply lands in that conversation. Another call with the same in_reply_to and new text posts a follow-up, " + "threaded under this session's first reply; the same text again posts nothing. dispatch read --message <id> reads " + "that conversation back. Every other message names its issue. " + `Body is at most ${DISPATCH_BODY_MAX} characters, the lines images appends included. ${ISSUE_REFERENCE}`,
14681
+ description: "Post a note humans must read now: a reply to a human's message or a deliverable that landed. A to-do only a human can " + "complete is an ask (dispatch ask), so it reaches their inbox. Never progress or status updates - Dispatch is a high-signal " + "record, not a log. Not a design decision (write it as a decision block in the document) or document feedback (dispatch comment). " + "To answer a human's direct message to this session - one sent from the Agents page, which names no issue - pass that message's bare id as " + "in_reply_to and no issue; the reply lands in that conversation. Another call with the same in_reply_to and new text posts a follow-up, " + "threaded under this session's first reply; the same text again posts nothing. dispatch read --message <id> reads " + "that conversation back. " + "Every other message names its issue; keep --issue whenever the frame you answer gave one. A BTW, Aside or Steer " + "frame carries a reply_with command: answer with it, on that issue. On Oh My Pi an Agents-page Send or Aside " + "arrives as a user message: answer in the conversation, with no dispatch message. " + `Body is at most ${DISPATCH_BODY_MAX} characters, the lines images appends included. ${ISSUE_REFERENCE}`,
14681
14682
  arguments: (z2) => ({
14682
14683
  issue: z2.string().describe(`${ISSUE_REFERENCE} Omit it only when in_reply_to answers a human's direct message to this session.`).optional(),
14683
14684
  body: z2.string({ max: DISPATCH_BODY_MAX }).describe(`Update text, at most ${DISPATCH_BODY_MAX} characters.`),
@@ -14694,7 +14695,7 @@ var dispatchToolSpecs = [
14694
14695
  ops: [{ op: "delete_column", block: "table-123", index: 1 }],
14695
14696
  precondition: { blocks: [{ id: "table-123", token: "sha256:current-table-token" }] }
14696
14697
  },
14697
- description: "Apply deterministic document edits: replace or delete quoted text, insert markdown at an anchor, retype an identified paragraph or typed block into a schema-declared typed block, delete or move a whole block by its id, or delete a table row or column in place. " + "Do not use it for review feedback or for reading; use dispatch comment, dispatch suggest, or dispatch doc-read instead. " + `For replace, delete, and quote anchors, find text as rendered: inline Markdown (**bold**, \`code\`) is tolerated and must be balanced; a leading '# ' matches a heading at any level. replace is inline: with is the new text of the matched span, so a marker of a different kind from the block's own stays literal text ('4. Design' written into a heading). A with that opens with a marker of the same kind as the matched block's own would write it twice and is INVALID_OP - including prose that merely looks like one ('1999. was a year' into an ordered item), which you write as text by escaping it ('1999\\. was a year'). The exception is a heading rename whose find carried a heading marker: replace(find="## Old", with="## New") gives '## New', and a different level applies only when find named the heading's actual level (find "## Old" with "### New" makes it an h3), since '# ' selects a heading without naming its level. A task item (\`- [ ]\` / \`- [x]\`) is ticked the same way: when find matches from the start of the item's text and with opens with '[x] ' or '[ ] ', the opening sets the item's box and the rest is its new text (replace(find="Write it", with="[x] Write it") ticks it); the same opening over a plain list item or a paragraph stays literal text. Any non-empty with that renders to no text - a line indented four spaces or a tab, which markdown reads as a code block, or whitespace alone - is INVALID_OP rather than a silent deletion; pass an empty with to delete the matched text on purpose - a list item, quote, typed block or footnote definition left holding only the emptied paragraph keeps it. ` + "with cannot open a new block: after a hard line break inside with (two trailing spaces, or a backslash, before the newline) a heading, bullet, '1.'/'1)' ordered, or '>' blockquote marker is INVALID_OP too, since that line would stay escaped text inside the matched block - use insert, plus delete for what it replaces, to add the block. A hard break in with is itself INVALID_OP when the matched text is in a heading or a table cell, which are written on one line. " + "A delete whose find is a block's entire text removes the block (a list emptied of its items goes too); delete with block removes any block by id, and move with block relocates one. A delete or retype that would take an ask block out of the document while its ask is open is refused, with nothing sent. delete_row and delete_column take a table block and a zero-based index, preserving the table block id and refusing to remove cells with open asks or unresolved comments. " + `Insert and move anchors also accept "start", "end", "heading:<exact heading text>", and "block:<id>"; dispatch doc-read --blocks lists every block's id, type, opening words and token, and each table's rows and cells with the indexes delete_row and delete_column take (GET /api/v1/artifacts/{artifact UUID}/blocks serves the same list). ` + "Optionally require the state just read: precondition selects exactly one of a document token from dispatch doc-read, or block {id, token} values from dispatch doc-read --blocks. A block guard must include every block the batch changes; Dispatch resolves quote targets and rejects an uncovered batch rather than applying it. Use a document token for insert or move, which depend on document order. Prefer block tokens when the covered content blocks are independent sections. Tokens include inline marks, so a fresh human comment also makes a stale edit fail. PRECONDITION_FAILED means re-read; EDIT_QUEUE_FULL means back off before retrying. " + "The result carries the document token this edit produced, so a chain of guarded edits passes each result's token as the next edit's precondition with no dispatch doc-read between them. " + "A batch that leaves the document exactly as it was mints no version, named or not, and the result says nothing changed and names each operation that did nothing. " + "A change a browser removes while the edit is in flight is never reported as applied: EDIT_LOST_TO_CONCURRENT_CHANGE means the write was refused and nothing was written, so re-read the document and decide again, as with PRECONDITION_FAILED; lost_ops on a successful result names operations whose text the live document no longer has, because the deletion landed after the version was written. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_POINTER}`,
14698
+ description: "Apply deterministic document edits: replace or delete quoted text, insert markdown at an anchor, retype an identified paragraph or typed block into a schema-declared typed block, delete or move a whole block by its id, or delete a table row or column in place. " + "Do not use it for review feedback or for reading; use dispatch comment, dispatch suggest, or dispatch doc-read instead. " + `For replace, delete, and quote anchors, find text as rendered: inline Markdown (**bold**, \`code\`) is tolerated and must be balanced; a leading '# ' matches a heading at any level. replace is inline: with is the new text of the matched span, so a marker of a different kind from the block's own stays literal text ('4. Design' written into a heading). A with that opens with a marker of the same kind as the matched block's own would write it twice and is INVALID_OP - including prose that merely looks like one ('1999. was a year' into an ordered item), which you write as text by escaping it ('1999\\. was a year'). The exception is a heading rename whose find carried a heading marker: replace(find="## Old", with="## New") gives '## New', and a different level applies only when find named the heading's actual level (find "## Old" with "### New" makes it an h3), since '# ' selects a heading without naming its level. A task item (\`- [ ]\` / \`- [x]\`) is ticked the same way: when find matches from the start of the item's text and with opens with '[x] ' or '[ ] ', the opening sets the item's box and the rest is its new text (replace(find="Write it", with="[x] Write it") ticks it); the same opening over a plain list item or a paragraph stays literal text. Any non-empty with that renders to no text - a line indented four spaces or a tab, which markdown reads as a code block, or whitespace alone - is INVALID_OP rather than a silent deletion; pass an empty with to delete the matched text on purpose - a list item, quote, typed block or footnote definition left holding only the emptied paragraph keeps it. ` + "with cannot open a new block: after a hard line break inside with (two trailing spaces, or a backslash, before the newline) a heading, bullet, '1.'/'1)' ordered, or '>' blockquote marker is INVALID_OP too, since that line would stay escaped text inside the matched block - use insert, plus delete for what it replaces, to add the block. A hard break in with is itself INVALID_OP when the matched text is in a heading or a table cell, which are written on one line. " + "A delete whose find is a block's entire text removes the block (a list emptied of its items goes too); delete with block removes any block by id, and move with block relocates one. A delete or retype that would take an ask block out of the document while its ask is open is refused, with nothing sent. delete_row and delete_column take a table block and a zero-based index, preserving the table block id and refusing to remove cells with open asks or unresolved comments. " + `Insert and move anchors also accept "start", "end", "heading:<exact heading text>", and "block:<id>"; dispatch doc-read --blocks lists every block's id, type, opening words and token, and each table's rows and cells with the indexes delete_row and delete_column take (GET /api/v1/artifacts/{artifact UUID}/blocks serves the same list). ` + "Optionally require the state just read: precondition selects exactly one of a document token from dispatch doc-read, or block {id, token} values from dispatch doc-read --blocks. A block guard must include every block the batch changes; Dispatch resolves quote targets and rejects an uncovered batch rather than applying it. Use a document token for insert or move, which depend on document order. Prefer block tokens when the covered content blocks are independent sections. Tokens include inline marks, so a fresh human comment also makes a stale edit fail. PRECONDITION_FAILED means re-read; EDIT_QUEUE_FULL means back off before retrying. " + "The result carries the document token this edit produced, so a chain of guarded edits passes each result's token as the next edit's precondition with no dispatch doc-read between them. " + "A batch that leaves the document exactly as it was mints no version, named or not, and the result says nothing changed and names each operation that did nothing. " + "A change a browser removes while the edit is in flight is never reported as applied: EDIT_LOST_TO_CONCURRENT_CHANGE means the write was refused and nothing was written, so re-read the document and decide again, as with PRECONDITION_FAILED; lost_ops on a successful result names operations whose text the live document no longer has, because the deletion landed after the version was written. " + "One replace per paragraph: more new paragraphs go in one insert after the last one you rewrote, since two inserts at one anchor come out reversed; read the passage back with dispatch doc-read afterwards. " + `The spec holds requirements, design, and decisions - never progress, status, or timestamps; a record of open items or progress is a document of its own. ${OWNER_REFERENCE} ${SPEC_WRITING_POINTER}`,
14698
14699
  arguments: (z2) => ({
14699
14700
  issue: z2.string().describe(ISSUE_REFERENCE).optional(),
14700
14701
  project: z2.string().describe("Project key owning the document.").optional(),
@@ -14741,12 +14742,12 @@ var dispatchToolSpecs = [
14741
14742
  {
14742
14743
  name: "dispatch_request_approval",
14743
14744
  example: { issue: "DSP-1", summary: "A live sync replaces the nightly export." },
14744
- description: "Ask a human to approve a document at its current version. Before calling, have a fresh read-only reader check the spec against the code and the human's words, then resolve every finding; a Legion architect uses `plan-gap-analyst`. Opens an approval ask (Approve / " + "Request changes) in the human's Inbox whose question names the document and version, " + "followed by the summary; the answer pins a review to that version and arrives as " + "artifact.approved or artifact.changes_requested. An open request follows the document: a " + "later version moves it to that version and leaves it waiting on you, as a human's reply in " + "its thread does. Only the first move since the request was opened or handed back sends an " + "event, and never to the session whose version made it; dispatch doc-read shows whom it " + "waits on. Once the revision is complete and " + HUMAN_AGREED_TO_DOCUMENT + ", call this again to hand that same Inbox row back. The request carries nothing new. A " + "call while it already waits on the human hands nothing back: the same summary changes " + "nothing, and a different one is refused, since it would rewrite the card the human is " + "reading. Approve and Request changes each close the request, so the next call opens a new " + "one. Call it once per revision, when the revision is complete, never after each edit. An " + "approval goes stale when the document changes outside its decision blocks after it: " + "request approval again once that revision is complete and " + HUMAN_AGREED_TO_DOCUMENT + ", with a one-sentence summary naming what changed since the earlier approval: the card " + "names that approval (version, date, reviewer) and its Show diff control carries the detail. " + "A human's own answer to a decision block carries the approval to the version it writes when " + "that answer is the whole change since the approved version, so nothing is requested for " + "it. A new version of a Legion root spec closes its armed design gate until a human " + "approves it; the carried approval reopens it too. " + "Refused, with nothing sent, while the document holds an open decision block, even when a " + "human asked for approval: the refusal names each block; ask the human to answer or waive " + "it first. " + OWNER_REFERENCE,
14745
+ description: "Ask a human to approve a document at its current version. Before calling, have a fresh read-only reader check the spec against the code and the human's words, then resolve every finding; a Legion architect runs `plan-gap-analyst` and `plan-reviewer`. Opens an approval ask (Approve / " + "Request changes) in the human's Inbox whose question names the document and version, " + "followed by the summary; the answer pins a review to that version and arrives as " + "artifact.approved or artifact.changes_requested. An open request follows the document: a " + "later version moves it to that version and leaves it waiting on you, as a human's reply in " + "its thread does. Only the first move since the request was opened or handed back sends an " + "event, and never to the session whose version made it; dispatch doc-read shows whom it " + "waits on. Once the revision is complete and " + HUMAN_AGREED_TO_DOCUMENT + ", call this again to hand that same Inbox row back. The request carries nothing new. A " + "call while it already waits on the human hands nothing back: the same summary changes " + "nothing, and a different one is refused, since it would rewrite the card the human is " + "reading. Approve and Request changes each close the request, so the next call opens a new " + "one. Call it once per revision, when the revision is complete, never after each edit. An " + "approval goes stale when the document changes outside its decision blocks after it: " + "request approval again once that revision is complete and " + HUMAN_AGREED_TO_DOCUMENT + ", with a one-sentence summary naming what changed since the earlier approval: the card " + "names that approval (version, date, reviewer) and its Show diff control carries the detail. " + "A human's own answer to a decision block carries the approval to the version it writes when " + "that answer is the whole change since the approved version, so nothing is requested for " + "it. A new version of a Legion root spec closes its armed design gate until a human " + "approves it; the carried approval reopens it too. " + "Refused, with nothing sent, while the document holds an open decision block, even when a " + "human asked for approval: the refusal names each block; ask the human to answer or waive " + "it first. " + OWNER_REFERENCE,
14745
14746
  arguments: (z2) => ({
14746
14747
  issue: z2.string().describe(ISSUE_REFERENCE).optional(),
14747
14748
  project: z2.string().describe("Project key owning the document.").optional(),
14748
14749
  artifact: z2.string().describe("Project document artifact id, slug, or filename; primary document by default for an issue.").optional(),
14749
- summary: z2.string({ min: 1 }).describe(`What the human is approving, in one to three sentences, and nothing else: no commentary on itself or the conversation, and no question; after an earlier approval, one sentence naming what changed since it, since the card shows that approval and a diff. Request approval only once ${HUMAN_AGREED_TO_DOCUMENT}.`)
14750
+ summary: z2.string({ min: 1 }).describe(`What the human is approving, in one to three sentences, and nothing else: no commentary on itself or the conversation, and no question; after an earlier approval, one sentence naming what changed since it, since the card shows that approval and a diff. A Legion architect adds one last sentence with its two spec reviews' results. Request approval only once ${HUMAN_AGREED_TO_DOCUMENT}.`)
14750
14751
  }),
14751
14752
  validation: documentOwnerValidation(true)
14752
14753
  },
@@ -14754,7 +14755,7 @@ var dispatchToolSpecs = [
14754
14755
  name: "dispatch_artifact",
14755
14756
  example: { issue: "DSP-1", name: "design.md", content: `# Design
14756
14757
  ` },
14757
- description: "Attach a local file or inline text as an issue artifact or project document. Do not use it to edit a live document; use " + "dispatch doc-edit instead. Exactly one of path or content is required; a markdown document is at most 1 MiB and any other file at most 25 MiB. " + "Markdown is also refused with 413 CAP_EXCEEDED when it makes more than 65,536 elements (a block weighs 3, and an empty list item, quote, footnote definition or typed block 3 more for the empty paragraph it holds; a table cell 4, a hard line break 3, an image 3, a piece of inline HTML 2, an autolink 2, a footnote reference 2; inline syntax, marks, lines of text and backslash escapes or character references 1 each), or when a new version would leave the document's stored markdown (what its text reads back as) longer than 1 MiB or making more than 65,536 elements, and longer or heavier than before; shorten it or split it across documents. " + "Markdown holding an ask block whose body breaks its content rule (one or more question paragraphs, then at most one bullet list of options, last) is refused with 400 INVALID_ASK_BLOCK; a new version of a document is held to it only for the asks it writes or changes. " + `${OWNER_REFERENCE}`,
14758
+ description: "Attach a local file or inline text as an issue artifact or project document. Do not use it to edit a live document; use " + "dispatch doc-edit instead. Never upload a document's own name to change it (spec.md replaces the spec whole); edit it with dispatch doc-edit. Uploading the same name again makes its next version. Exactly one of path or content is required; a markdown document is at most 1 MiB and any other file at most 25 MiB. " + "Markdown is also refused with 413 CAP_EXCEEDED when it makes more than 65,536 elements (a block weighs 3, and an empty list item, quote, footnote definition or typed block 3 more for the empty paragraph it holds; a table cell 4, a hard line break 3, an image 3, a piece of inline HTML 2, an autolink 2, a footnote reference 2; inline syntax, marks, lines of text and backslash escapes or character references 1 each), or when a new version would leave the document's stored markdown (what its text reads back as) longer than 1 MiB or making more than 65,536 elements, and longer or heavier than before; shorten it or split it across documents. " + "Markdown holding an ask block whose body breaks its content rule (one or more question paragraphs, then at most one bullet list of options, last) is refused with 400 INVALID_ASK_BLOCK; a new version of a document is held to it only for the asks it writes or changes. " + `${OWNER_REFERENCE}`,
14758
14759
  arguments: (z2) => ({
14759
14760
  issue: z2.string().describe(ISSUE_REFERENCE).optional(),
14760
14761
  project: z2.string().describe("Project key for an unlinked document.").optional(),
@@ -14779,7 +14780,7 @@ var dispatchToolSpecs = [
14779
14780
  issue: z2.string().describe(ISSUE_REFERENCE).optional(),
14780
14781
  project: z2.string().describe("Project key owning the document.").optional(),
14781
14782
  artifact: z2.string().describe("Document artifact id, slug, or filename: one of the issue's, or a project document.").optional(),
14782
- ref: z2.string().describe("Optional dispatch:// issue or document reference.").optional(),
14783
+ ref: z2.string().describe("Optional dispatch:// reference: dispatch://KEY, dispatch://KEY/spec, " + "dispatch://KEY/artifact/<slug>[@vN], dispatch://KEY/ask/<id>, dispatch://KEY/comment/<id>, " + "dispatch://KEY/message/<id>, dispatch://PROJECT/artifact/<slug>[@vN], and its " + "/ask/<id> and /comment/<id>; or the dashboard URL of one.").optional(),
14783
14784
  message: z2.string().describe("A message id (uuid): reads the conversation it belongs to, the root message and every " + "reply. Name nothing else with it.").optional(),
14784
14785
  events: z2.number({ int: true, min: 1 }).describe(`How many of the issue's newest events an issue read or its dispatch://KEY/log lists, oldest first; default ${DEFAULT_ISSUE_READ_EVENTS}. Name no --all-events with it.`).optional(),
14785
14786
  all_events: z2.boolean().describe(`List every one of the issue's events, oldest first, on an issue read or its dispatch://KEY/log, in place of the last ${DEFAULT_ISSUE_READ_EVENTS}. Name no --events with it.`).optional()
@@ -14811,7 +14812,7 @@ var dispatchToolSpecs = [
14811
14812
  {
14812
14813
  name: "dispatch_issues",
14813
14814
  example: { project: "PROJ", limit: 250, offset: 250 },
14814
- description: "List a project's issues for a roadmap or backlog pass: every issue in one project, each carrying " + "its status, priority, parent, labels, open-ask count, progress (its spec's task-list items and " + "its direct children, each done of total, as `tasks 3/7 \xB7 children 2/5`, each only when it has " + "any), and route with whether it reaches anyone, " + "so you can see backlog shape without opening every issue. Optionally filter by status, parent, " + "label, priority, route status, or how recently it changed; priority takes one or more of 0-3 " + "(P0-P3) and null for an issue with no priority, so an owner's P0/P1 audit is priority [0, 1]. " + 'route_status "no_holder" lists every open issue whose route names a role nobody holds or a ' + "session that is not running at the moment of the read, whatever its priority. A restarting " + "session is absent for minutes, so an issue is unowned only when a read ten minutes later agrees. " + "Do not use it to search by keyword or phrase; dispatch search remains the keyword surface. " + "Dispatch pages the list: limit sets the page size (default " + `${DEFAULT_ISSUE_PAGE_LIMIT}, max ${MAX_ISSUE_PAGE_LIMIT}) and offset selects where it starts ` + "(default 0), and the answer names how many issues match, so repeat with the next offset to " + "walk every matching issue. A walk is exact only while the list does not change: an issue " + "that enters or leaves what the filters match, or whose status or rank changes, between two " + "pages shifts rows across a page boundary, so one issue can come back twice and another never. " + "all takes the whole filtered list in one read instead, for a caller that must not miss or " + "repeat a row while other writers change the list, and especially for one whose own writes " + "move issues out of what it filtered on.",
14815
+ description: "List a project's issues for a roadmap or backlog pass: every issue in one project, each carrying " + "its status, priority, parent, labels, open-ask count, progress (its spec's task-list items and " + "its direct children, each done of total, as `tasks 3/7 \xB7 children 2/5`, each only when it has " + "any), and route with whether it reaches anyone, " + "so you can see backlog shape without opening every issue. Optionally filter by status, parent, " + "label, priority, route status, or how recently it changed; priority takes one or more of 0-3 " + "(P0-P3) and null for an issue with no priority, so an owner's P0/P1 audit is priority [0, 1]. " + 'route_status "no_holder" lists every open issue whose route names a role nobody holds or a ' + "session that is not running, as of the server's copy of the listener's session list, at " + "most 5 s old, whatever its priority. A restarting " + "session is absent for minutes, so an issue is unowned only when a read ten minutes later agrees. " + "Do not use it to search by keyword or phrase; dispatch search remains the keyword surface. " + "Dispatch pages the list: limit sets the page size (default " + `${DEFAULT_ISSUE_PAGE_LIMIT}, max ${MAX_ISSUE_PAGE_LIMIT}) and offset selects where it starts ` + "(default 0), and the answer names how many issues match, so repeat with the next offset to " + "walk every matching issue. A walk is exact only while the list does not change: an issue " + "that enters or leaves what the filters match, or whose status or rank changes, between two " + "pages shifts rows across a page boundary, so one issue can come back twice and another never. " + "all takes the whole filtered list in one read instead, for a caller that must not miss or " + "repeat a row while other writers change the list, and especially for one whose own writes " + "move issues out of what it filtered on.",
14815
14816
  arguments: (z2) => ({
14816
14817
  project: z2.string().describe("Project key to list issues from."),
14817
14818
  status: z2.enum(ISSUE_STATUSES).describe("Optional lifecycle status filter.").optional(),
@@ -14845,7 +14846,7 @@ var dispatchToolSpecs = [
14845
14846
  {
14846
14847
  name: "dispatch_architecture_sync",
14847
14848
  example: { project: "CORE", repo: "acme/infra" },
14848
- description: "Import a project's architecture model from its source repositories now, instead of waiting " + "for the server's five-minute schedule. A project may have several sources, one per " + "repository, and its model is every source's components together. Without repo every " + "source imports and each answers on its own line; with repo only that source does. Each " + "line names the imported commit, or the recorded error when that source's model was " + "rejected (its previous components stay up, and every other source is untouched). Sources " + "are saved in Settings or with PUT /api/v1/projects/{key}/architecture-sources/{owner}/{repo}; " + "404 SOURCE_NOT_FOUND when the project has none, or none for that repository.",
14849
+ description: "Import a project's architecture model from its source repositories now, instead of waiting " + "for the server's five-minute schedule. A project may have several sources, one per " + "repository, and its model is every source's components together. Without repo every " + "source imports and each answers on its own line; with repo only that source does. Each " + "line names the imported commit, or the recorded error when that source's model was " + "rejected (its previous components stay up, and every other source is untouched). A failed " + "line also says what of that source still shows (its commit, its component count when it " + "was saved again on another branch and has not imported from it yet, or that it never " + "imported), and a credential or branch problem is that source's failed line, or with repo " + "the error 409 SOURCE_ACCESS. Sources " + "are saved in Settings or with PUT /api/v1/projects/{key}/architecture-sources/{owner}/{repo}; " + "404 SOURCE_NOT_FOUND when the project has none, or none for that repository.",
14849
14850
  arguments: (z2) => ({
14850
14851
  project: z2.string().describe("Project key whose architecture sources to sync, such as CORE."),
14851
14852
  repo: z2.string().describe("One source repository of the project to sync alone, owner/name; omit it to sync every source.").optional()
@@ -17046,7 +17047,7 @@ async function bootLegionClaim(host2) {
17046
17047
  // package.json
17047
17048
  var package_default = {
17048
17049
  name: "@sjawhar/pi-legion",
17049
- version: "11.0.0",
17050
+ version: "11.1.0",
17050
17051
  license: "Apache-2.0",
17051
17052
  type: "module",
17052
17053
  omp: {
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: leading-a-work-stream
3
+ description: Use when you lead a work stream or team of agents, when an agent asks you to approve, confirm or review its step or pull request, when you want to know the state of the stream, when an agent starts a long project, or when you consider building tooling or a restriction for agents.
4
+ ---
5
+
6
+ # Leading a work stream
7
+
8
+ If this skill disagrees with the code, a command's output or its `--help`, follow those and say which line of this skill is wrong.
9
+
10
+ A lead raises the stream's numbers by making its agents faster, and is not a gate: agents waiting on your reply are a queue, and a queue is the stream's largest hidden cost. Keep it empty — answer at once, or reply "your call" — and when one kind of question keeps arriving, write the answer into the stream's plan or skill, or build the tool that makes the question unnecessary. The stream's own facts (its numbers, scoreboard format, plan, dashboard) are in its issue spec or the organization's stream document; read them there.
11
+
12
+ In a Legion tree, the daemon's workflow and your role prompt decide who starts a worker, who accepts the work and how it merges. This skill does not change that sequence. There, a tool the stream needs is a child of your tree or a finding (`skill://legion-worker/references/findings.md`), not an agent you start on your own. Why each rule below works: [sources.md](skill://leading-a-work-stream/references/sources.md).
13
+
14
+ ## What a lead does
15
+
16
+ 1. **Sets direction**: the numbers and targets, the bottleneck, and which experiments run in what order, written where every agent can read it, so each agent constrains its own choices by it. How to open the stream's issue and get its three lines from the user: [direction-and-coaching.md](skill://leading-a-work-stream/references/direction-and-coaching.md).
17
+ 2. **Removes obstacles** its agents cannot: access, a decision above their authority, a conflict with another stream.
18
+ 3. **Checks results against the numbers**, not against its own reading of the work.
19
+ 4. **Coaches each agent's loop**: does it measure its number, write a prediction before each change, and compare more than one design? Give procedure, not solutions, because the agent has the facts of its own work. Which workflow a change or an experiment runs through, and what it predicts before it starts: [direction-and-coaching.md](skill://leading-a-work-stream/references/direction-and-coaching.md).
20
+ 5. **Makes the stream faster**: finds where agents lose time and changes the skill, tool or step that causes it.
21
+ 6. **Proves its facts with a command.** A claim about what exists or what a thing can do rests on a command and its output, not on a reading. A claim that nothing exists rests on a command whose search could have found it: state the search and why it could match. A lead's facts about its own stream are the ones nobody else checks.
22
+ 7. **Keeps the stream's record of what each agent accepted.** At each check-in, read it against the agents' issues and open asks. An item that is neither done, in progress, nor an open ask is dropped work. The lead finds its owner and has it recorded at once.
23
+
24
+ ## The speed loop
25
+
26
+ Arm a reminder that wakes you every 3 hours and run the six-step loop it names: [speed-tick.md](skill://leading-a-work-stream/references/speed-tick.md). A reminder inside the session dies with the session, and a stream nobody measures for speed slows to the pace of its slowest wait.
27
+
28
+ ## Dates
29
+
30
+ A date is the time the work takes at agent speed, plus only the waits nobody can shorten. Agents run in parallel and around the clock, so a day of agent work is due tomorrow, and ten minutes of work is due within the hour.
31
+
32
+ - Each date carries its basis in one line ("about 3 h of work, 2 h of benchmark runs"), so padding is visible.
33
+ - A key performance indicator (KPI) is one of the stream's numbers in its issue spec. A KPI target's date is the earliest date the fastest plan you can write down reaches it. Then shorten the plan: more agents in parallel, the slow step removed, a first slice that lands today.
34
+ - When a date will slip, say so the moment you know, with the new date and why. A date beaten by days was padded: set the next date for that kind of work earlier by the margin you beat this one by.
35
+
36
+ ## What a lead does not do
37
+
38
+ - Gate a decision an agent can make. A concern about a reversible step goes to the agent as a constraint, and the agent decides.
39
+ - Review every pull request. Peer review and tests are the approval.
40
+ - Require a plan, spec or confirmation to be sent to it before an agent acts.
41
+ - Ask agents for a report of each decision or step. Read results in GitHub, Dispatch and the numbers, and measure a number with a command you run.
42
+ - Ration a shared resource (disk, VMs, compute) by approving each use. Set the limit, and the agents apply it themselves or through a tool.
43
+ - Relay messages between agents who can talk directly.
44
+ - Give an agent orders specific to its own work (which task to launch, at what size, what to regrade): the agent has the facts of its own work and the lead does not. Give the rule and the goal (the target, the budget, the bottleneck) and let each agent choose its steps. Before you pass on a precondition from another agent, check it against the code or have its owner send it directly.
45
+
46
+ ## Decision rights
47
+
48
+ A step that is easy to undo is the agent's: it acts and reports to nobody, and you read the result in GitHub, Dispatch and the numbers. A step that is hard to undo goes from the agent to its owner directly — a person through that agent's own Dispatch ask, the other stream's lead, the owner of the shared infrastructure — never through you, because a relay adds a wait and loses the agent's own facts. Which steps fall where: [decision-rights.md](skill://leading-a-work-stream/references/decision-rights.md).
49
+
50
+ When an agent asks permission for an easy-to-undo step, reply "your call" and state any constraint it must keep; add no check of your own before it may act. Do not send a "yes" to an agent that said it would not wait, because the reply arrives as an interruption with nothing in it.
51
+
52
+ ## Rules reach agents through what they read
53
+
54
+ A rule sent only by message reaches the agents online that day, in the lead's paraphrase, and is gone for the next session. Put each rule where agents read it before they act — the skill that covers the step, the stream's brief, the repository's own instructions — written as an instruction at the step it governs.
55
+
56
+ Keep one record of the words of the person the stream works for, each quoted with its date and source, and link that quote wherever a text says the person wants something ([user-quotes.md](skill://leading-a-work-stream/references/user-quotes.md)). Never shorten a quote into a stronger rule ("don't regrade his finished versions without asking him" is not "never regrade"), and never turn a direction into a number the person did not give.
57
+
58
+ ## Keep every request and wait in Dispatch
59
+
60
+ You answer for every request the user gives any of your agents, and for every wait any of them has.
61
+
62
+ - **A request from the user goes on a claimed Dispatch issue before work on it starts**: a line in that issue's `open-items.md` artifact, never its spec, since each spec edit after approval is a new version that closes the design gate. Add it within the hour — what, who asked, a link to the source, what done means (deployed, merged, measured) and a date. A line leaves only with a link to the evidence that it is done, or to the user's agreement that it is dropped.
63
+ - **Done is delivered and checked: you saw the result yourself** (deployed, merged, measured). Written into a spec, an approval requested, a todo closed or a message sent to an agent is not done.
64
+ - **A wait on a person is an open Dispatch ask the moment you know it** — never only a chat question, a note, a peer message, a comment on an answered ask, or a review request, because nobody tracks those. A wait on anything else is an `open-items.md` line naming the blocker, its owner and its link. Keep working on everything else.
65
+ - **Every hour, run the stream's Dispatch check,** the command its brief names. **Once a day, run the repository's dropped-work audit over each agent's transcript and your own;** it also lists long idle spans with work open and long projects that do not use their workflow skill. Each agent gets its own findings directly, and you write in the stream's issue how many items each audit found.
66
+ - Three more rules of this upkeep — checking a delegated result, a step held back from a hand-over, and what to re-read after a restart — are in [dispatch-upkeep.md](skill://leading-a-work-stream/references/dispatch-upkeep.md).
67
+
68
+ ## Plan the day with the team
69
+
70
+ Every morning by 09:00 Pacific, you and your agents agree on Envoy what the stream will merge today, and you write that list into the stream's record, checking that each item has an agent that committed to land it; without it each agent optimises its own day and the stream's numbers move by accident. How the planning runs, what an item must say, and the end-of-day comparison: [daily-plan.md](skill://leading-a-work-stream/references/daily-plan.md).
71
+
72
+ ## Knowing the state of the stream
73
+
74
+ From records you read yourself (GitHub, Dispatch, the numbers), answer: what is the target; what is the actual value now; what is the obstacle, and which one is being worked on; what is the next step and what should it do to the number; when will we know? Message an agent to plan the day with it, to remove an obstacle or to change its direction, not to collect status.
75
+
76
+ ## Before you say something is blocked
77
+
78
+ **Read the thing itself, not the thing next to it.** Before you write that X is blocked by Y, read X's own state: `gh pr checks` at the head, not the queue's length; the process (`ps -eo pid,etimes,args | grep <session-id>`) and the session's last tool call, not a registry's session list; the job's own start time, not the pool's history. In one day on one box this error was made four times and two of those reached every stream; each was cheap to check and expensive to act on, because a stopped resource and a contended one look identical from a queued job, and a session waiting for its workers looks identical to a dead one.
79
+
80
+ **A blocker you relay to other streams carries the command that shows it.** Without it, each stream spends a check to disprove it after acting on it.
81
+
82
+ **Ask a fresh reviewer, for each claimed cause, to name the command or record that would show it FALSE, and to run it.** A reviewer that reads only the prose confirms a confident wrong cause. The causes that cost most are the ones nobody could falsify, because nobody looked for what would disprove them.
83
+
84
+ **Run a watch that names quiet sessions, and let it restart nothing.** A session that dies mid-task stays dead until a person notices: measured at about 7 hours on a day when one credential failure stopped a box, and at 5 minutes or less once a watch existed. A watch reads the session's transcript modification time, its process and, if it can, its pane; it reports, and the owner decides. Two traps: a systemd user service on a dev box may not reach the tmux server at all (it answers `server exited unexpectedly` while the same command works from a pane), and a watch built on an agent-messaging registry reports live sessions as dead under load. Test it by making it report something you know is true.
85
+
86
+ ## Delegation and tooling
87
+
88
+ - Use subagents and peer agents for review, research and skill improvement. Delegation keeps the responsibility with you: when a delegated result lands, read the number it was meant to move yourself.
89
+ - When an agent starts a long project, check that it uses the workflow skill for that kind of work and name the skill: the `/sdd` workflow for an engineering project, `building-and-delivering-tasks` for RL task work. That skill's own review steps replace review by the lead.
90
+ - When agents keep losing time to the same chore, start an agent to build the tool that removes it. Do not wait to be asked.
91
+ - Do not build, file or ask for a restriction, guard or gate against a failure nobody has seen. Fix a real failure at its cause, after it happens.
92
+ - When the user asks the stream for an explainer video, you own it: have a subagent make it with the `making-explainer-videos` skill.
93
+
94
+ ## Improving this skill
95
+
96
+ When a rule here failed or was missing, edit this file directly, and put the evidence in the commit message, so the next lead gets the fix instead of the same failure.
@@ -0,0 +1,8 @@
1
+ # Planning the day with the team
2
+
3
+ Every morning, by 09:00 Pacific, the lead and its agents agree on Envoy what the stream will merge today, as a working team does at its daily planning: each agent says what it can land and what stands in the way, the team settles the list, and the lead writes it into the stream's record. The lead owns the list and that it is real.
4
+
5
+ - Each item starts with the key performance indicator (KPI, one of the stream's numbers) it moves and the change expected, for example "Grade wall time 7,104 s -> 5,700 s: ...", so the day can be checked against the numbers.
6
+ - Work that moves no KPI goes on the list only when it unblocks an item that does, and names that item.
7
+ - During the day, an agent that will miss an item says so as soon as it knows, so the team can move work.
8
+ - At the end of the day, post each item's actual KPI reading next to its prediction in the stream's record, and ask the owner of each missed item on Envoy what blocked it.
@@ -0,0 +1,6 @@
1
+ # Decision rights
2
+
3
+ | The step | Who decides |
4
+ | --- | --- |
5
+ | Easy to undo: a code change, a flag, a branch, opening or merging a pull request that passed peer review and tests, a planned experiment, and any spending the workflow itself calls for (a paid-API review, compute, a VM, a model run the task needs) | The agent. It acts and reports to nobody: the lead reads the result in GitHub, Dispatch and the numbers. |
6
+ | Hard to undo: deleting data someone else created, a new financial commitment (a contract, a subscription), a message outside the organization, security, another stream's work or shared state | The agent takes it to its owner directly: a person through the agent's own Dispatch ask, the other stream's agent or lead, the owner of the shared infrastructure. It does not pass through the lead. |
@@ -0,0 +1,13 @@
1
+ # Setting direction and coaching the loop
2
+
3
+ ## The stream's three lines
4
+
5
+ Open the stream's issue with three lines above its targets: the problem the stream exists to solve, the one approach it commits to, and what it will not do. Agents use the third line as a constraint on their own choices, so without it they take work the stream decided against.
6
+
7
+ Ask the user those three questions once, in one message, each with a draft answer taken from the user's own words. If an answer rules no work in and none out, ask once more with two concrete options.
8
+
9
+ ## Coaching an agent's loop
10
+
11
+ - An experiment on a number, with a benchmark that runs in minutes, goes through `/autoresearch`: the expected gain is in its notes before each run, and the change is run again before it is kept, so a result that came from noise does not survive.
12
+ - Use the `ce-optimize` skill instead when the result is noisy or has several objectives.
13
+ - A one-off change still gets its prediction written in the scoreboard before it starts, so a wrong prediction is visible instead of explained away afterwards.
@@ -0,0 +1,7 @@
1
+ # Dispatch upkeep: the rest of the lead's rules
2
+
3
+ These go with the "Keep every request and wait in Dispatch" section of the skill.
4
+
5
+ - **Check a delegated or relayed result against its done before you report it**, because an agent's "done" is often a message sent, not a thing delivered. Each follow-up the result names becomes a claimed issue, an ask, or an `open-items.md` line, the same hour.
6
+ - **A step you keep back from a hand-over becomes its own claimed issue,** blocked by the handed-over one, or it is lost when the hand-over closes.
7
+ - **After a restart or a compaction, first re-read your claimed issues and your open asks** (`dispatch open-asks`), then the transcript since your last audit, because what you were waiting for is not in the new context.
@@ -0,0 +1,9 @@
1
+ # Why these rules
2
+
3
+ - Bezos, [2015 shareholder letter](https://www.sec.gov/Archives/edgar/data/1018724/000119312516530910/d168744dex991.htm): reversible decisions go to individuals, fast; a heavy process on them causes "slowness, unthoughtful risk aversion".
4
+ - DORA, [Streamlining change approval](https://dora.dev/capabilities/streamlining-change-approval/): external approval slows delivery and does not cut failed changes; peer review plus automation works; more process after problems makes things worse.
5
+ - US Army, [ADP 6-0 Mission Command](https://armypubs.army.mil/epubs/DR_pubs/DR_a/ARN47032-ADP_6-0-000-WEB-1.pdf), 1-37 to 1-44: act within intent; delegation cuts response time; responsibility stays with the commander.
6
+ - Google re:Work, [research behind great managers](https://rework.withgoogle.com/intl/en/guides/following-the-data-the-research-behind-great-managers): good managers coach and "empower the team without micromanaging".
7
+ - Rother & Liker, [A Kata for PDCA](https://cdn.prod.website-files.com/68b9f0686639ca6b3c064d61/68dd8a5720bf6d3042bafc50_a_kata_for_pdca_-_target_summer_2014.pdf), and [LEI, Kata](https://www.lean.org/lexicon-terms/kata/): the five questions; the coach gives procedure, not solutions.
8
+ - Reinertsen, [Product Development Flow, ch. 1](http://lpd2.com/wp-content/uploads/2013/06/ReinertsenFLOWChap1.pdf): queues are the largest hidden cost.
9
+ - [Summary of Grove's High Output Management](https://www.becausetech.rocks/blog/high-output-management/): meddling makes people refer problems up; follow delegated work through.