@sjawhar/pi-legion-envoy 0.29.0 → 0.30.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/README.md CHANGED
@@ -121,8 +121,8 @@ The shared contract supplies the model-facing schemas and descriptions. The
121
121
  feedback, documents, artifacts, and status reads.
122
122
 
123
123
  `dispatch_artifact` accepts exactly one upload source: a local `path`, or inline `content`.
124
- For example, an architect can post its primary specification with
125
- `{ issue, name: "spec.md", content: "# Design", primary: true }`.
124
+ For example, an architect can post a specification directly with
125
+ `{ issue, name: "spec.md", content: "# Design" }`.
126
126
 
127
127
  Lifecycle and scope decisions between Legion roles go through `envoy_publish` to the owning
128
128
  architect's role topic; Dispatch is for durable questions to the human and the shared
package/dist/envoy.js CHANGED
@@ -29700,6 +29700,16 @@ function dispatchToolSchema(spec, z, opts) {
29700
29700
  return spec.validation === undefined ? z.object(shape, opts) : z.refineObject(shape, spec.validation.check, spec.validation.message, opts);
29701
29701
  }
29702
29702
  var ISSUE_REFERENCE = "An issue is a native KEY or external owner/repo#n reference; an external reference creates its native issue in the repository's dashboard-configured project or, failing that, the default project (DISPATCH_DEFAULT_PROJECT).";
29703
+ var SPEC_SECTIONS = [
29704
+ "Decisions needed",
29705
+ "Acceptance",
29706
+ "Requirements",
29707
+ "Design",
29708
+ "Errors",
29709
+ "Testing",
29710
+ "Rejected"
29711
+ ];
29712
+ var SPEC_WRITING_GUIDANCE = `When writing a spec, use these sections in order: ${SPEC_SECTIONS.join(", ")}. ` + "Every line is a fact, decision, or risk; use tables over prose; see skills/dispatch Writing a spec.";
29703
29713
  var ASK_URGENCIES = ["low", "med", "high", "blocking"];
29704
29714
  var DOC_EDIT_OPS = ["replace", "delete", "insert"];
29705
29715
  var dispatchToolSpecs = [
@@ -29711,7 +29721,7 @@ var dispatchToolSpecs = [
29711
29721
  title: z.string().describe("Concise issue title."),
29712
29722
  parent: z.string().describe("Optional parent issue.").optional(),
29713
29723
  external: z.string().describe("Optional external issue reference.").optional(),
29714
- spec: z.string().describe("Optional initial primary-document markdown.").optional()
29724
+ spec: z.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_GUIDANCE}`).optional()
29715
29725
  })
29716
29726
  },
29717
29727
  {
@@ -29777,7 +29787,7 @@ var dispatchToolSpecs = [
29777
29787
  },
29778
29788
  {
29779
29789
  name: "dispatch_doc_edit",
29780
- description: "Apply deterministic text edits to a document. Do not use it for review feedback or for reading; use " + `dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. ${ISSUE_REFERENCE}`,
29790
+ description: "Apply deterministic text edits to a document. Do not use it for review feedback or for reading; use " + `dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. ${ISSUE_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
29781
29791
  arguments: (z) => ({
29782
29792
  issue: z.string().describe(ISSUE_REFERENCE),
29783
29793
  artifact: z.string().describe("Artifact slug or id for the document."),
@@ -29811,7 +29821,6 @@ var dispatchToolSpecs = [
29811
29821
  name: z.string().describe("Artifact filename shown in Dispatch."),
29812
29822
  path: z.string().describe("Local path to the file to upload.").optional(),
29813
29823
  content: z.string().describe("Inline text to store as a Markdown document.").optional(),
29814
- primary: z.boolean().describe("Make this document the issue primary artifact.").optional(),
29815
29824
  summary: z.string().describe("Optional version summary.").optional()
29816
29825
  }),
29817
29826
  validation: {
@@ -31197,8 +31206,6 @@ class DispatchClient {
31197
31206
  return this.#json("POST", artifactPath, input);
31198
31207
  const form = new FormData;
31199
31208
  form.set("name", input.name);
31200
- if (input.primary !== undefined)
31201
- form.set("primary", String(input.primary));
31202
31209
  if (input.summary !== undefined)
31203
31210
  form.set("summary", input.summary);
31204
31211
  if (input.actor !== undefined)
@@ -31717,20 +31724,17 @@ Open anchored asks/comments: ${marks.join(", ")}`,
31717
31724
  };
31718
31725
  }
31719
31726
  case "dispatch_artifact": {
31720
- const primary = optionalBoolean(args, "primary");
31721
31727
  const summary = optionalString(args, "summary");
31722
31728
  const name = stringArg(args, "name");
31723
31729
  const content = optionalString(args, "content");
31724
31730
  const result = await client.artifact(issue(), content === undefined ? {
31725
31731
  name,
31726
31732
  file: Bun.file(resolvePath(input.cwd, stringArg(args, "path"))),
31727
- ...primary === undefined ? {} : { primary },
31728
31733
  ...summary === undefined ? {} : { summary },
31729
31734
  actor
31730
31735
  } : {
31731
31736
  name,
31732
31737
  content,
31733
- ...primary === undefined ? {} : { primary },
31734
31738
  ...summary === undefined ? {} : { summary },
31735
31739
  actor
31736
31740
  });
package/dist/legion.js CHANGED
@@ -29698,6 +29698,16 @@ function dispatchToolSchema(spec, z, opts) {
29698
29698
  return spec.validation === undefined ? z.object(shape, opts) : z.refineObject(shape, spec.validation.check, spec.validation.message, opts);
29699
29699
  }
29700
29700
  var ISSUE_REFERENCE = "An issue is a native KEY or external owner/repo#n reference; an external reference creates its native issue in the repository's dashboard-configured project or, failing that, the default project (DISPATCH_DEFAULT_PROJECT).";
29701
+ var SPEC_SECTIONS = [
29702
+ "Decisions needed",
29703
+ "Acceptance",
29704
+ "Requirements",
29705
+ "Design",
29706
+ "Errors",
29707
+ "Testing",
29708
+ "Rejected"
29709
+ ];
29710
+ var SPEC_WRITING_GUIDANCE = `When writing a spec, use these sections in order: ${SPEC_SECTIONS.join(", ")}. ` + "Every line is a fact, decision, or risk; use tables over prose; see skills/dispatch Writing a spec.";
29701
29711
  var ASK_URGENCIES = ["low", "med", "high", "blocking"];
29702
29712
  var DOC_EDIT_OPS = ["replace", "delete", "insert"];
29703
29713
  var dispatchToolSpecs = [
@@ -29709,7 +29719,7 @@ var dispatchToolSpecs = [
29709
29719
  title: z.string().describe("Concise issue title."),
29710
29720
  parent: z.string().describe("Optional parent issue.").optional(),
29711
29721
  external: z.string().describe("Optional external issue reference.").optional(),
29712
- spec: z.string().describe("Optional initial primary-document markdown.").optional()
29722
+ spec: z.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_GUIDANCE}`).optional()
29713
29723
  })
29714
29724
  },
29715
29725
  {
@@ -29775,7 +29785,7 @@ var dispatchToolSpecs = [
29775
29785
  },
29776
29786
  {
29777
29787
  name: "dispatch_doc_edit",
29778
- description: "Apply deterministic text edits to a document. Do not use it for review feedback or for reading; use " + `dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. ${ISSUE_REFERENCE}`,
29788
+ description: "Apply deterministic text edits to a document. Do not use it for review feedback or for reading; use " + `dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. ${ISSUE_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
29779
29789
  arguments: (z) => ({
29780
29790
  issue: z.string().describe(ISSUE_REFERENCE),
29781
29791
  artifact: z.string().describe("Artifact slug or id for the document."),
@@ -29809,7 +29819,6 @@ var dispatchToolSpecs = [
29809
29819
  name: z.string().describe("Artifact filename shown in Dispatch."),
29810
29820
  path: z.string().describe("Local path to the file to upload.").optional(),
29811
29821
  content: z.string().describe("Inline text to store as a Markdown document.").optional(),
29812
- primary: z.boolean().describe("Make this document the issue primary artifact.").optional(),
29813
29822
  summary: z.string().describe("Optional version summary.").optional()
29814
29823
  }),
29815
29824
  validation: {
@@ -31636,8 +31645,6 @@ class DispatchClient {
31636
31645
  return this.#json("POST", artifactPath, input);
31637
31646
  const form = new FormData;
31638
31647
  form.set("name", input.name);
31639
- if (input.primary !== undefined)
31640
- form.set("primary", String(input.primary));
31641
31648
  if (input.summary !== undefined)
31642
31649
  form.set("summary", input.summary);
31643
31650
  if (input.actor !== undefined)
@@ -32156,20 +32163,17 @@ Open anchored asks/comments: ${marks.join(", ")}`,
32156
32163
  };
32157
32164
  }
32158
32165
  case "dispatch_artifact": {
32159
- const primary = optionalBoolean(args, "primary");
32160
32166
  const summary = optionalString(args, "summary");
32161
32167
  const name = stringArg(args, "name");
32162
32168
  const content = optionalString(args, "content");
32163
32169
  const result = await client.artifact(issue(), content === undefined ? {
32164
32170
  name,
32165
32171
  file: Bun.file(resolvePath(input.cwd, stringArg(args, "path"))),
32166
- ...primary === undefined ? {} : { primary },
32167
32172
  ...summary === undefined ? {} : { summary },
32168
32173
  actor
32169
32174
  } : {
32170
32175
  name,
32171
32176
  content,
32172
- ...primary === undefined ? {} : { primary },
32173
32177
  ...summary === undefined ? {} : { summary },
32174
32178
  actor
32175
32179
  });
@@ -12,6 +12,38 @@ The server enforces high signal: an ask question is at most 800 characters with
12
12
  options; comment and message bodies are at most 2,000 characters; an artifact is at most 25 MiB.
13
13
  It refuses over-limit input; it never truncates it. GitHub threads and markers no longer exist.
14
14
 
15
+ ## Writing a spec
16
+
17
+ A spec is a decision record for the human who decides and the implementer who builds, not a
18
+ transcript of your thinking. Use exactly these document headings in this order.
19
+
20
+ | Section | Required content | Form |
21
+ | --- | --- | --- |
22
+ | **Decisions needed** | Only decisions requiring human authority, taste, or risk appetite. Each states one question, two or three options with tradeoffs, and a recommendation. Every item is an anchored `dispatch_ask`. Answered items move into Requirements with provenance, then leave this section. No other section asks the reader anything. Empty means `None.` | One decision per line; anchor each ask to that line. |
23
+ | **Acceptance** | Every outcome names its check and user-facing surface. An outcome without a verification method is not acceptance criteria. | Numbered lines; browser scenario, API call, or CLI command. |
24
+ | **Requirements** | Provenance is a verbatim human quote or `inferred: <reasoning>`; readers treat inferred requirements as hypotheses. Do not restate the prompt in prose. | `requirement \| provenance` table. |
25
+ | **Design** | State the files, components, routes, and data flow that change. | Facts, not narrative; diagrams only for genuine structure. |
26
+ | **Errors** | Name the behaviour for every error condition; never specify a silent fallback. | `condition \| behaviour` table. |
27
+ | **Testing** | Map every acceptance line to the proof that exercises it. | Suite or scenario. |
28
+ | **Rejected** | Record each considered alternative and why it was rejected so it is not proposed again. | One alternative per line. |
29
+
30
+ ### Rules
31
+
32
+ - Every sentence is a fact, decision, or risk; delete the rest.
33
+ - Use tables over prose and keep one idea per line.
34
+ - Do not use Overview, Background, Introduction, Summary, or Conclusion sections.
35
+ - Do not hedge with “might” or “could consider.”
36
+ - Do not use TBD, TODO, or placeholders; an open item is a Decision needed.
37
+ - Keep each section to one screen; work that exceeds one screen per section is two specs.
38
+ - Update the spec in place as decisions land. The spec is the record; comments are the discussion.
39
+
40
+ ### Self-review
41
+
42
+ - [ ] No placeholders remain.
43
+ - [ ] No sections conflict.
44
+ - [ ] The spec covers one implementation plan's worth of work.
45
+ - [ ] Every requirement has exactly one reading.
46
+
15
47
  ## Your issue
16
48
 
17
49
  Every session works on an issue. Legion pre-fills `issue` from `LEGION_ISSUE`: use a native issue key
@@ -26,6 +58,7 @@ dispatch_issue({ project, title, parent?, external?, spec? })
26
58
  ```
27
59
  It returns `details` `{ issue, topic }`. Use `dispatch_issue` only to create an issue; never use
28
60
  it to park a question.
61
+ When `spec` is supplied, follow [Writing a spec](#writing-a-spec).
29
62
 
30
63
  ## Asking
31
64
 
@@ -65,6 +98,7 @@ answer. Use `reply_to_ask` on `dispatch_comment` to reply under your own ask; it
65
98
  exclusive with `reply_to`.
66
99
 
67
100
  ## The spec is where narrative goes
101
+ Write and update the issue specification according to [Writing a spec](#writing-a-spec).
68
102
 
69
103
  Read the current document before changing it:
70
104
 
@@ -72,7 +106,7 @@ Read the current document before changing it:
72
106
  dispatch_doc_read({ issue?, artifact?, version?, ref? })
73
107
  ```
74
108
  It returns live or versioned markdown with open marks and `details` `{ issue }`; omit `artifact`
75
- with `issue` to read the primary document. Then write narrative with:
109
+ with `issue` to read the issue specification. Then write narrative with:
76
110
 
77
111
  ```ts
78
112
  dispatch_doc_edit({ issue, artifact, ops, summary? })
@@ -119,27 +153,26 @@ dispatch_suggest({ issue, artifact, quote, replace_with, body?, occurrence? })
119
153
  It returns `details` `{ issue, topic, comment }`. A human accepts or rejects a suggestion. On
120
154
  `TARGET_AMBIGUOUS`, add zero-based `occurrence`. On `TARGET_NOT_FOUND`, re-read the document
121
155
  before retrying. `INVALID_OP` names a malformed edit; `CAP_EXCEEDED` never truncates;
122
- `ISSUE_CLOSED` rejects a write. `ACTOR_KIND`, `ROUTE_INVALID`, and `PRIMARY_NOT_DOC` reject an
123
- invalid actor, route, or primary artifact.
156
+ `ISSUE_CLOSED` rejects a write. `ACTOR_KIND` and `ROUTE_INVALID` reject an invalid actor or route.
124
157
 
125
158
  ## Artifacts
126
159
 
127
160
  Attach an image, diagram, or local file with:
128
161
 
129
162
  ```ts
130
- dispatch_artifact({ issue, name, path, primary?, summary? })
163
+ dispatch_artifact({ issue, name, path, summary? })
131
164
  ```
132
165
 
133
166
  Or, when the text is already in the call, post a Markdown document directly:
134
167
 
135
168
  ```ts
136
- dispatch_artifact({ issue, name: "spec.md", content: "# Design\n...", primary: true })
169
+ dispatch_artifact({ issue, name: "spec.md", content: "# Design\n..." })
137
170
  ```
138
171
 
139
172
  Exactly one of `path` and `content` is required. The inline form sends JSON with
140
173
  `Content-Type: application/json`. It returns `details` `{ issue, topic, artifact, version }`.
141
- Uploading the same `name` creates its next version. Use `content` for an architect's primary spec;
142
- set `primary: true` only for Markdown documents.
174
+ Uploading the same `name` creates its next version. Use `content` when the text is already in
175
+ the call.
143
176
 
144
177
  ## Messages
145
178
 
@@ -70,13 +70,15 @@ exercise a criterion end to end, building that path is a child issue of this tre
70
70
  relationship from `parent`. Keep the returned issue keys in ordered waves; a child is
71
71
  inert until released.
72
72
 
73
+ Specifications written into Dispatch follow [`skills/dispatch`'s Writing a spec](../dispatch/SKILL.md#writing-a-spec).
74
+
73
75
  Write one root specification containing the accepted scope, adoption/decomposition,
74
76
  waves, acceptance criteria, and integration test. When the config-armed root design gate
75
77
  applies, run this exact sequence **before any Legion-role spawn**, including a
76
78
  sub-architect:
77
79
 
78
80
  ```text
79
- dispatch_artifact({ issue: "<root issue>", name: "spec.md", content: "<root specification>", primary: true, summary: "<one-line summary>" })
81
+ dispatch_artifact({ issue: "<root issue>", name: "spec.md", content: "<root specification>", summary: "<one-line summary>" })
80
82
  askId = dispatch_ask({
81
83
  issue: "<root issue>",
82
84
  question: "<specification summary and the decision requested>",
@@ -118,6 +118,8 @@ path-scoped workflow.
118
118
 
119
119
  ## Phase work
120
120
 
121
+ Specifications written into Dispatch follow [`skills/dispatch`'s Writing a spec](../dispatch/SKILL.md#writing-a-spec).
122
+
121
123
  Follow the repository's normal engineering workflow and the assigned issue's acceptance
122
124
  criteria. Your phase's own charter and the predecessor handoffs you read define the phase
123
125
  artifact and its completion evidence. Do not replace architect-owned decomposition, gate
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "0.29.0",
3
+ "version": "0.30.0",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [