@sjawhar/opencode-legion-envoy 3.19.7 → 3.19.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/server.js +36 -11
- package/package.json +1 -1
- package/skills/dispatch/SKILL.md +38 -36
- package/skills/dispatch/references/asks.md +14 -7
- package/skills/dispatch/references/examples.md +30 -15
- package/skills/legion-architect/SKILL.md +3 -2
- package/skills/legion-worker/references/merge-gate.md +5 -4
package/dist/src/server.js
CHANGED
|
@@ -13894,6 +13894,8 @@ function dispatchToolSchema(spec, z2, opts) {
|
|
|
13894
13894
|
}
|
|
13895
13895
|
var ISSUE_REFERENCE = "An issue is a native KEY or external owner/repo#n reference. An external reference addresses an existing Dispatch issue, including one linked to that GitHub pull request; only dispatch_issue with external creates a native issue.";
|
|
13896
13896
|
var OWNER_REFERENCE = "Exactly one of issue and project is required. An issue is a native KEY or external owner/repo#n reference; a project is a project key such as CORE and addresses an unlinked project document named by artifact.";
|
|
13897
|
+
var ASK_QUESTION_CONTRACT = "The question carries the problem the reader recognises and why it matters now, what constrains " + "the answer, and the recommendation with its reason. It asks how to solve the problem or which " + "outcome is wanted; never enumerate choices in the question.";
|
|
13898
|
+
var ASK_OPTIONS_CONTRACT = "Options carry the genuinely different approaches. Each option has a label, and its description " + "says what that approach costs.";
|
|
13897
13899
|
function documentOwnerValidation(requireArtifact, alwaysRequireArtifact = false) {
|
|
13898
13900
|
return {
|
|
13899
13901
|
check: (value) => {
|
|
@@ -14041,18 +14043,31 @@ var dispatchToolSpecs = [
|
|
|
14041
14043
|
},
|
|
14042
14044
|
{
|
|
14043
14045
|
name: "dispatch_ask",
|
|
14044
|
-
example: {
|
|
14045
|
-
|
|
14046
|
+
example: {
|
|
14047
|
+
issue: "DSP-1",
|
|
14048
|
+
question: "The release cannot pass its review gate because the revised plan is unreviewed. " + "How should we proceed? Recommendation: review the plan before release to keep the review gate.",
|
|
14049
|
+
options: [
|
|
14050
|
+
{
|
|
14051
|
+
label: "Review the revised plan",
|
|
14052
|
+
description: "Delays release for review but keeps the release gate."
|
|
14053
|
+
},
|
|
14054
|
+
{
|
|
14055
|
+
label: "Release without review",
|
|
14056
|
+
description: "Ships sooner but bypasses the review gate."
|
|
14057
|
+
}
|
|
14058
|
+
]
|
|
14059
|
+
},
|
|
14060
|
+
description: "Open a durable, answerable decision on an issue or project document. Do not use it 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 you are blocked on a human for, including a credential or grant to renew, an " + "approval, or a decision, is an ask, never a message. " + "Anchor a document question, 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 ` + `and has at most 8 options. ${OWNER_REFERENCE}`,
|
|
14046
14061
|
arguments: (z2) => ({
|
|
14047
14062
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
14048
14063
|
project: z2.string().describe("Project key owning the document.").optional(),
|
|
14049
14064
|
artifact: z2.string().describe("Project document artifact id, slug, or filename.").optional(),
|
|
14050
14065
|
ref: z2.string().describe("Optional dispatch:// reference (issue, document, message, or ask); appended to the question and rendered as a link.").optional(),
|
|
14051
|
-
question: z2.string({ max: ASK_QUESTION_MAX }).describe(
|
|
14066
|
+
question: z2.string({ max: ASK_QUESTION_MAX }).describe(`${ASK_QUESTION_CONTRACT} At most ${ASK_QUESTION_MAX} characters.`),
|
|
14052
14067
|
options: z2.array(z2.object({
|
|
14053
14068
|
label: z2.string().describe("Selectable option label."),
|
|
14054
|
-
description: z2.string().describe("
|
|
14055
|
-
}), { max: 8 }).describe(
|
|
14069
|
+
description: z2.string().optional().describe("What this option costs.")
|
|
14070
|
+
}), { max: 8 }).optional().describe(`${ASK_OPTIONS_CONTRACT} Up to 8 objects { label, description? }.`),
|
|
14056
14071
|
multiple: z2.boolean().describe("Whether multiple choices may be selected.").optional(),
|
|
14057
14072
|
urgency: z2.enum(ASK_URGENCIES).describe("Optional decision urgency.").optional(),
|
|
14058
14073
|
anchor: z2.object({
|
|
@@ -14067,16 +14082,26 @@ var dispatchToolSpecs = [
|
|
|
14067
14082
|
name: "dispatch_edit_ask",
|
|
14068
14083
|
example: {
|
|
14069
14084
|
ask: "01234567-0000-4000-8000-000000000001",
|
|
14070
|
-
question: "
|
|
14085
|
+
question: "The release cannot pass its review gate because the revised plan is unreviewed. " + "How should we proceed? Recommendation: review the plan before release to keep the review gate.",
|
|
14086
|
+
options: [
|
|
14087
|
+
{
|
|
14088
|
+
label: "Review the revised plan",
|
|
14089
|
+
description: "Delays release for review but keeps the release gate."
|
|
14090
|
+
},
|
|
14091
|
+
{
|
|
14092
|
+
label: "Release without review",
|
|
14093
|
+
description: "Ships sooner but bypasses the review gate."
|
|
14094
|
+
}
|
|
14095
|
+
]
|
|
14071
14096
|
},
|
|
14072
|
-
description: "Edit an open question in place. Use it to correct or refine the same decision; retract the " + "old ask and open a new one when the decision itself changes. Previous text remains in the
|
|
14097
|
+
description: "Edit an open question in place. Use it to correct or refine the same decision; retract the " + "old ask and open a new one when the decision itself changes. " + ASK_QUESTION_CONTRACT + " " + ASK_OPTIONS_CONTRACT + " Previous text remains in the event log. Only the asking session can edit it; answered or " + "resolved asks cannot be edited. An ask that lives as an `ask` block in a document is written " + "in the document too, changing only the fields you name - pass urgency alone and the " + "question's wording, formatting, links and comment anchors are untouched - so the edit " + "writes a document version and closes a spec's design gate until that version is approved; " + "text the block cannot carry back unchanged is refused, naming the field - an option label " + 'containing ": ", the separator between a label and its description, is one example.',
|
|
14073
14098
|
arguments: (z2) => ({
|
|
14074
14099
|
ask: z2.string().describe("Ask id (uuid); an 8+ hex prefix unique among this session's own open asks works too."),
|
|
14075
|
-
question: z2.string({ max: ASK_QUESTION_MAX }).describe(
|
|
14100
|
+
question: z2.string({ max: ASK_QUESTION_MAX }).optional().describe(`${ASK_QUESTION_CONTRACT} Replaces the ask's question; at most ${ASK_QUESTION_MAX} characters.`),
|
|
14076
14101
|
options: z2.array(z2.object({
|
|
14077
14102
|
label: z2.string().describe("Selectable option label."),
|
|
14078
|
-
description: z2.string().describe("
|
|
14079
|
-
}), { max: 8 }).describe(
|
|
14103
|
+
description: z2.string().optional().describe("What this option costs.")
|
|
14104
|
+
}), { max: 8 }).optional().describe(`${ASK_OPTIONS_CONTRACT} Replaces the ask's options; up to 8 objects { label, description? }.`),
|
|
14080
14105
|
multiple: z2.boolean().describe("Whether multiple choices may be selected.").optional(),
|
|
14081
14106
|
urgency: z2.enum(ASK_URGENCIES).describe("Replacement decision urgency.").optional()
|
|
14082
14107
|
}),
|
|
@@ -14238,7 +14263,7 @@ var dispatchToolSpecs = [
|
|
|
14238
14263
|
name: "dispatch_artifact",
|
|
14239
14264
|
example: { issue: "DSP-1", name: "design.md", content: `# Design
|
|
14240
14265
|
` },
|
|
14241
|
-
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;
|
|
14266
|
+
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 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}`,
|
|
14242
14267
|
arguments: (z2) => ({
|
|
14243
14268
|
issue: z2.string().describe(ISSUE_REFERENCE).optional(),
|
|
14244
14269
|
project: z2.string().describe("Project key for an unlinked document.").optional(),
|
package/package.json
CHANGED
package/skills/dispatch/SKILL.md
CHANGED
|
@@ -10,9 +10,9 @@ decide, never a log of your work. The transcript is your scratch pad; progress a
|
|
|
10
10
|
goes through a `dispatch_*` tool.
|
|
11
11
|
|
|
12
12
|
The server enforces high signal: an ask question is at most 800 characters with at most eight options; comment and message bodies are at
|
|
13
|
-
most 2,000 characters;
|
|
14
|
-
the 800-character limit (850/800)`); it never truncates it. A tool call with several problems is
|
|
15
|
-
(`<tool> was not called: N problems`), so one corrected call lands. GitHub threads and markers no
|
|
13
|
+
most 2,000 characters; a markdown document is at most 1 MiB and any other file at most 25 MiB. It refuses over-limit input with the number
|
|
14
|
+
to trim (`question is 50 characters over the 800-character limit (850/800)`); it never truncates it. A tool call with several problems is
|
|
15
|
+
refused once, every problem listed (`<tool> was not called: N problems`), so one corrected call lands. GitHub threads and markers no
|
|
16
16
|
longer exist.
|
|
17
17
|
|
|
18
18
|
## Where the detail lives
|
|
@@ -49,9 +49,11 @@ not share this session's vocabulary, and is often on a phone. Write for that per
|
|
|
49
49
|
- Expand every identifier the first time it appears: an issue key gets its title, a PR number its
|
|
50
50
|
title, a file what it is for, a session id who it is. Link a URL rather than pasting a bare id.
|
|
51
51
|
- A question lives in the spec or discussion it came from, placed as
|
|
52
|
-
[Decision blocks](#decision-blocks) says, never as a compressed standalone ask.
|
|
53
|
-
the
|
|
54
|
-
|
|
52
|
+
[Decision blocks](#decision-blocks) says, never as a compressed standalone ask. The question
|
|
53
|
+
carries the problem the reader recognises and why it matters now, what constrains the answer, and
|
|
54
|
+
the recommendation with its reason. It asks how to solve the problem or which outcome is wanted;
|
|
55
|
+
never enumerate choices in the question. The options carry the genuinely different approaches.
|
|
56
|
+
Each option has a label, and its description says what that approach costs.
|
|
55
57
|
- Describe a change by what its reader stands to lose, not by what the system does. The
|
|
56
58
|
engineering sentence names the change; the reader's sentence names who can do what today, what
|
|
57
59
|
they will not be able to do after it, what still works, and what you cannot tell. It is a
|
|
@@ -84,7 +86,7 @@ a new version that keeps the human's own text, never a second "spec" artifact be
|
|
|
84
86
|
versions keep the history.
|
|
85
87
|
- **No placeholders.** No TBD, TODO, or hedging ("might", "could consider"): an open item is a
|
|
86
88
|
decision block, a technical decision your lane makes and records in the text, or, for a
|
|
87
|
-
contract between two lanes, a question
|
|
89
|
+
contract between two lanes, a question you settle with the other lane over Envoy (see
|
|
88
90
|
[Before you ask](#before-you-ask) under Asking).
|
|
89
91
|
- **No progress.** The spec records the design and its decisions, never status, timestamps, an
|
|
90
92
|
"Update HH:MMZ" section, a pull-request list, or handoff notes. Progress is not a Dispatch
|
|
@@ -208,15 +210,15 @@ Every `dispatch_ask` passes four gates first:
|
|
|
208
210
|
1. **Does it need his authority, taste, or risk appetite?** This is the bar for a decision
|
|
209
211
|
written as an `:::ask` block in context ([Decision blocks](#decision-blocks)). Technical
|
|
210
212
|
decisions inside your outcome do not: schema shapes, table layouts, field names, and migration
|
|
211
|
-
internals are your lane's to decide and record in the spec. A contract between two lanes
|
|
212
|
-
|
|
213
|
-
IAM, deletion or exposure of production data, anything that reaches a customer) passes this
|
|
214
|
-
gate: it is your own `dispatch_ask` to Sami on your own issue
|
|
215
|
-
platform PO.
|
|
213
|
+
internals are your lane's to decide and record in the spec. A contract between two lanes is
|
|
214
|
+
settled by those two lanes over Envoy, and you open no ask for it. A halt condition (a change
|
|
215
|
+
to IAM, deletion or exposure of production data, anything that reaches a customer) passes this
|
|
216
|
+
gate: it is your own `dispatch_ask` to Sami on your own issue.
|
|
216
217
|
2. **Is there genuine uncertainty, and have you measured what you can?** If there is none, it is
|
|
217
218
|
a plan you execute. The one legitimate ask without uncertainty is permission for an action
|
|
218
|
-
only a human can authorise — a production write, an external send, a console action
|
|
219
|
-
the
|
|
219
|
+
only a human can authorise — a production write, an external send, a console action. Start it
|
|
220
|
+
with the problem and why the action is needed, then state what the action changes and risks;
|
|
221
|
+
its options name the outcomes.
|
|
220
222
|
Measure before you write: how many are affected, whether anything reaches the path, what the
|
|
221
223
|
current state already is. The measurement decides whether a human is needed at all, and when
|
|
222
224
|
one is, it turns a research request he cannot answer into a decision he can. Put the
|
|
@@ -247,8 +249,8 @@ Every `dispatch_ask` passes four gates first:
|
|
|
247
249
|
(further down) lists that the delivery waits on — including a conflict between what he asked
|
|
248
250
|
for and another of his rules, which this gate would otherwise bury as settled.
|
|
249
251
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
+
Nobody audits or retracts another session's asks: passing every gate, and carrying the content
|
|
253
|
+
instead of pointing at another message in prose (below), is the asking session's own check.
|
|
252
254
|
|
|
253
255
|
Open a decision with:
|
|
254
256
|
```ts
|
|
@@ -268,13 +270,11 @@ It returns `details` `{ issue, ask, follows: { ask } }` for an issue or `{ proje
|
|
|
268
270
|
|
|
269
271
|
References belong in the question text; `ref` is sugar that appends its `dispatch://` value to the question as a rendered link.
|
|
270
272
|
|
|
271
|
-
An ask is read on a phone by someone who has not read the code.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
the ask to the document passage instead. Apply the phone test from "Writing for the human" before
|
|
277
|
-
posting. Anchor a document question with `anchor: { artifact, quote, occurrence? }`; `occurrence`
|
|
273
|
+
An ask is read on a phone by someone who has not read the code. Write its question and options as
|
|
274
|
+
[Writing for the human](#writing-for-the-human) says, and apply its phone test before posting.
|
|
275
|
+
Never put file paths, line numbers, sequence numbers, document versions, or role tokens in the
|
|
276
|
+
question; if the human needs that detail, anchor the ask to the document passage instead. Anchor a
|
|
277
|
+
document question with `anchor: { artifact, quote, occurrence? }`; `occurrence`
|
|
278
278
|
is zero-based and selects a repeated quote. A quote anchor is pinned to its lowest complete
|
|
279
279
|
containing block while retaining its quote as display text, so rewording the passage keeps it
|
|
280
280
|
attached; a quote spanning top-level blocks, and existing anchors without a block, stay readable
|
|
@@ -289,11 +289,11 @@ message above", or "as attached".
|
|
|
289
289
|
issue's comments. The rules:
|
|
290
290
|
|
|
291
291
|
- An ask that names another message in prose — "my comment above", "the procedure I posted",
|
|
292
|
-
"see the earlier message" —
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
292
|
+
"see the earlier message" — fails the gates. Put the content IN the ask. If it does not fit the
|
|
293
|
+
800-character budget, the step is too big: split the step, never point elsewhere. The only
|
|
294
|
+
pointers an ask may carry are a `dispatch://` reference or a document `anchor`, and they cite —
|
|
295
|
+
the ask still says in one line what the reader will find there and can be answered without
|
|
296
|
+
following them.
|
|
297
297
|
- Expand every term the reader has not used first. A product name, an internal setting, an
|
|
298
298
|
acronym, a value you coined this session — write what it is in the ask, in his words.
|
|
299
299
|
- A runbook the human must execute is one ask per step, each self-contained: what to do, where,
|
|
@@ -308,19 +308,21 @@ they must read to decide belongs in the spec in the first place — see [Artifac
|
|
|
308
308
|
|
|
309
309
|
### When you need a human
|
|
310
310
|
|
|
311
|
-
Before saying you are waiting for human input, call `dispatch_open_asks`. With no arguments it lists this session's active asks across open issues and project documents, including whether the human or agent owes the next reply. With `dispatch_open_asks({ project })` it lists every open ask in that project — on its issues and on its documents, whoever authored them — which is how you
|
|
311
|
+
Before saying you are waiting for human input, call `dispatch_open_asks`. With no arguments it lists this session's active asks across open issues and project documents, including whether the human or agent owes the next reply. With `dispatch_open_asks({ project })` it lists every open ask in that project — on its issues and on its documents, whoever authored them — which is how you see what a whole project is waiting on rather than just your own asks.
|
|
312
312
|
|
|
313
|
-
**Unsettled product shape needs a decision before implementation.** When a page, navigation entry, table key, customer-scoping rule, or persisted sidecar would set product shape that Sami has not already settled, send a one-line ask before the first implementation commit. A lane's schema decision or a
|
|
313
|
+
**Unsettled product shape needs a decision before implementation.** When a page, navigation entry, table key, customer-scoping rule, or persisted sidecar would set product shape that Sami has not already settled, send a one-line ask before the first implementation commit. A lane's schema decision or a contract two lanes agree does not settle product shape. This does not turn a user-specified decision or routine implementation into an approval request. A control or behaviour the human asked for in words is settled by those words, together with every choice inside it that his words do not make (where it sits, its defaults, its options): build it without an ask, as gate 4 of [Before you ask](#before-you-ask) says. This rule covers only product shape outside what he asked for, and its ask comes before the commit that sets that shape.
|
|
314
314
|
|
|
315
315
|
**Anything you are blocked on a human for is an open ask.** An agent waits on a human only through
|
|
316
316
|
an open ask. An approval, a credential or grant to renew, a setting only they can change, a review
|
|
317
317
|
click, a decision, or a conflict between two of their own rules: open a `dispatch_ask` the moment
|
|
318
|
-
you know
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
318
|
+
you know. Start with the problem and why it matters, then the constraints, options and their
|
|
319
|
+
costs, and your recommendation. For an action only the human can perform, state what it changes
|
|
320
|
+
and risks; never make the action itself the question. Never write it into a spec, a comment reply,
|
|
321
|
+
a message, or a pull-request body: nothing in those paths reaches the human's Inbox, and a human
|
|
322
|
+
who is not reading your document does not know they are the blocker. Before asking, try to remove
|
|
323
|
+
the step: a value already on the machine, a permission you already hold, an API that replaces the
|
|
324
|
+
click. One ask per item, `urgency: "high"` when work is stopped on it; while it is open, keep
|
|
325
|
+
working on everything that is not.
|
|
324
326
|
|
|
325
327
|
Once an ask is open (who answers it, handing a human a to-do, editing, retracting or resolving it,
|
|
326
328
|
answering a clarification, whose turn a reply gives), see
|
|
@@ -15,14 +15,21 @@ It returns `details` `{ session, owner, service }`: `owner` is the lowercase log
|
|
|
15
15
|
|
|
16
16
|
## Handing a human a to-do, editing, resolving, and replying
|
|
17
17
|
|
|
18
|
-
A to-do handed to a human
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
description, and the human's free-text answer
|
|
18
|
+
A to-do handed to a human still follows the question and options contract that `skill://dispatch`'s
|
|
19
|
+
"Writing for the human" states. For a human-only action, what it changes and risks constrains the
|
|
20
|
+
answer. There is no fixed vocabulary and the server treats no label specially.
|
|
21
|
+
If an outcome needs a reason, say so in that option's description, and the human's free-text answer
|
|
22
|
+
carries it:
|
|
22
23
|
```ts
|
|
23
|
-
dispatch_ask({
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
dispatch_ask({
|
|
25
|
+
issue: "DSP-42",
|
|
26
|
+
question:
|
|
27
|
+
"The verified fix remains unavailable in production because deployment is pending. Deploying changes production and could expose a deployment problem. How should we proceed? Recommendation: deploy the verified fix now because it restores the intended behavior.",
|
|
28
|
+
options: [
|
|
29
|
+
{ label: "Deploy the verified fix", description: "Restores the fix but can expose a deployment problem." },
|
|
30
|
+
{ label: "Hold the deploy", description: "Avoids a production change now but leaves production without the fix." },
|
|
31
|
+
],
|
|
32
|
+
})
|
|
26
33
|
```
|
|
27
34
|
|
|
28
35
|
Correct or refine an open ask in place instead of opening a second question:
|
|
@@ -8,22 +8,21 @@ message that should not be sent, and a draft placed where the human reads it.
|
|
|
8
8
|
Before — a wall of text hides the decision and makes the choices unclickable:
|
|
9
9
|
|
|
10
10
|
```text
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
I think waiting is safest but the customer demo is tomorrow and the list above is probably stale.
|
|
11
|
+
The deploy branch has the migration and dashboard changes. Staging is fine, but release notes are
|
|
12
|
+
not reviewed before tomorrow's customer demo. Review the notes, ship without them, or cut the
|
|
13
|
+
dashboard from the release. Waiting is safest, but the list above may be stale.
|
|
15
14
|
```
|
|
16
15
|
|
|
17
|
-
After —
|
|
16
|
+
After — state the problem and make each genuinely different option a button:
|
|
18
17
|
|
|
19
18
|
```ts
|
|
20
19
|
dispatch_ask({
|
|
21
20
|
issue: "LEGION-815",
|
|
22
21
|
question:
|
|
23
|
-
"
|
|
22
|
+
"Release notes are unreviewed, and tomorrow's customer demo means the release cannot wait for a later review. How should we proceed? Recommendation: review the notes, then ship, to keep the release complete and reviewed.",
|
|
24
23
|
options: [
|
|
25
|
-
{ label: "Review notes, then ship", description: "
|
|
26
|
-
{ label: "Ship now", description: "Meets the demo deadline
|
|
24
|
+
{ label: "Review notes, then ship", description: "Delays release for review but keeps the release complete and reviewed." },
|
|
25
|
+
{ label: "Ship now", description: "Meets the demo deadline but leaves the release notes unreviewed." },
|
|
27
26
|
],
|
|
28
27
|
urgency: "high",
|
|
29
28
|
anchor: { artifact: "spec", quote: "Release requires reviewed operator instructions before deployment." },
|
|
@@ -54,11 +53,19 @@ dispatch_artifact({ issue: "OPS-52", name: "cu-update-2026-09-15.md", content: "
|
|
|
54
53
|
dispatch_doc_edit({ issue: "OPS-52", artifact: "spec", ops: [
|
|
55
54
|
{ op: "insert", after: "## Context", markdown: "## Draft (artifact cu-update-2026-09-15.md)" },
|
|
56
55
|
]})
|
|
57
|
-
dispatch_ask({
|
|
56
|
+
dispatch_ask({
|
|
57
|
+
issue: "OPS-52",
|
|
58
|
+
question:
|
|
59
|
+
"Customers need an update today, but the draft is only in a separate file, so the reader cannot review it in context. How should we proceed? Recommendation: put the draft in the spec before sending it.",
|
|
60
|
+
options: [
|
|
61
|
+
{ label: "Put the draft in the spec", description: "Adds a spec edit before sending but lets the reader review it in context." },
|
|
62
|
+
{ label: "Keep the separate file", description: "Saves the spec edit but leaves the reader to find the draft." },
|
|
63
|
+
],
|
|
64
|
+
})
|
|
58
65
|
```
|
|
59
66
|
|
|
60
67
|
After — the draft is a section of the spec, and the ask anchors there. If it really must be a
|
|
61
|
-
file
|
|
68
|
+
file, the spec and the ask both link the slug from the upload result:
|
|
62
69
|
|
|
63
70
|
```ts
|
|
64
71
|
dispatch_doc_edit({ issue: "OPS-52", artifact: "spec", ops: [
|
|
@@ -66,17 +73,25 @@ dispatch_doc_edit({ issue: "OPS-52", artifact: "spec", ops: [
|
|
|
66
73
|
]})
|
|
67
74
|
dispatch_ask({
|
|
68
75
|
issue: "OPS-52",
|
|
69
|
-
question:
|
|
70
|
-
|
|
76
|
+
question:
|
|
77
|
+
"Customers need an update today, and the reviewed text is ready in the spec. Sending it cannot be recalled. How should we proceed? Recommendation: send the reviewed update.",
|
|
78
|
+
options: [
|
|
79
|
+
{ label: "Send the reviewed update", description: "Delivers the update today but makes its text external." },
|
|
80
|
+
{ label: "Hold the update", description: "Avoids sending now but leaves customers without the update." },
|
|
81
|
+
],
|
|
71
82
|
anchor: { artifact: "spec", quote: "Hi team," },
|
|
72
83
|
})
|
|
73
84
|
// or, for a real file — the spec links it where the reader needs it, and so does the ask:
|
|
74
85
|
dispatch_doc_edit({ issue: "OPS-52", artifact: "spec", ops: [
|
|
75
|
-
{ op: "insert", after: "## Context", markdown: "## Draft\n\nThe update to send
|
|
86
|
+
{ op: "insert", after: "## Context", markdown: "## Draft\n\nThe update to send: dispatch://OPS-52/artifact/cu-update-2026-09-15-md" },
|
|
76
87
|
]})
|
|
77
88
|
dispatch_ask({
|
|
78
89
|
issue: "OPS-52",
|
|
79
|
-
question:
|
|
80
|
-
|
|
90
|
+
question:
|
|
91
|
+
"Customers need an update today, and its reviewed text is linked from the spec. Sending it cannot be recalled. How should we proceed? Recommendation: send the reviewed update.",
|
|
92
|
+
options: [
|
|
93
|
+
{ label: "Send the reviewed update", description: "Delivers the linked update today but makes its text external." },
|
|
94
|
+
{ label: "Hold the update", description: "Avoids sending now but leaves customers without the update." },
|
|
95
|
+
],
|
|
81
96
|
})
|
|
82
97
|
```
|
|
@@ -262,8 +262,9 @@ Preserve this order exactly:
|
|
|
262
262
|
task. It drives the changed path in production through the user's own access path and records
|
|
263
263
|
what it saw on the pull request and on this issue. Close only after the implementer's production
|
|
264
264
|
report exists. A defect it finds is a corrective child issue of this tree, not a note on a
|
|
265
|
-
closed one;
|
|
266
|
-
with
|
|
265
|
+
closed one; if the implementer cannot perform the deploy, it opens a `dispatch_ask` that starts
|
|
266
|
+
with the production gap and why it matters, then names the required step, its risk, and
|
|
267
|
+
outcome-named options. The issue waits for that answer.
|
|
267
268
|
|
|
268
269
|
What returns the tree to review: a changed diff — a commit above the approved head that
|
|
269
270
|
touches anything outside `docs/solutions/`, or a conflict-resolution merge whose fingerprint
|
|
@@ -96,7 +96,8 @@ completion leaves the issue in reviewing until you finish.
|
|
|
96
96
|
carrying the Legion footer, and a `dispatch_message` on the issue — the reviewer and merger
|
|
97
97
|
read GitHub, the architect reads the issue. When the deploy that carries the merge has not
|
|
98
98
|
happened (a shared profile still holding the previous plugin release, a daemon still running
|
|
99
|
-
the previous commit, a slot nobody has run), open a `dispatch_ask`
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
the previous commit, a slot nobody has run), open a `dispatch_ask` that starts with the
|
|
100
|
+
production gap and why it matters, then names the required install or restart step, its risk,
|
|
101
|
+
and outcome-named options. Keep the `Production:` line at `pending <what is missing>`, and
|
|
102
|
+
complete the check once the human answers. Never record a staging pass as the production check,
|
|
103
|
+
and never let the architect sign off on a `pending` line.
|