@sjawhar/opencode-legion-envoy 11.4.0 → 12.0.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/dist/dispatch.js +133 -33
- package/dist/src/server.js +12 -3
- package/package.json +1 -1
- package/skills/dispatch/SKILL.md +5 -2
- package/skills/legion-architect/SKILL.md +1 -1
- package/skills/legion-architect/references/design-gate.md +9 -9
- package/skills/legion-controller/references/admission.md +1 -1
- package/skills/legion-controller/references/wakes.md +5 -6
package/dist/dispatch.js
CHANGED
|
@@ -14009,6 +14009,13 @@ var CommentDeliverySchema = object({
|
|
|
14009
14009
|
reply_id: string2().nullable(),
|
|
14010
14010
|
created_at: string2()
|
|
14011
14011
|
});
|
|
14012
|
+
var ReplyBlockSchema = object({
|
|
14013
|
+
node: string2(),
|
|
14014
|
+
ref: string2().optional(),
|
|
14015
|
+
block_id: string2(),
|
|
14016
|
+
text: string2(),
|
|
14017
|
+
removed: boolean2()
|
|
14018
|
+
});
|
|
14012
14019
|
var CommentEventPayloadSchema = object({
|
|
14013
14020
|
id: string2().optional(),
|
|
14014
14021
|
artifact_name: string2().optional(),
|
|
@@ -14034,6 +14041,7 @@ var CommentEventPayloadSchema = object({
|
|
|
14034
14041
|
created_at: string2().optional(),
|
|
14035
14042
|
mentions: array(CommentMentionSchema).optional(),
|
|
14036
14043
|
deliveries: array(CommentDeliverySchema).optional(),
|
|
14044
|
+
reply_block: ReplyBlockSchema.nullish(),
|
|
14037
14045
|
thread_logins: array(string2()).optional()
|
|
14038
14046
|
}).passthrough();
|
|
14039
14047
|
var MessageEventPayloadSchema = object({
|
|
@@ -14044,6 +14052,7 @@ var MessageEventPayloadSchema = object({
|
|
|
14044
14052
|
reply_body: string2().optional(),
|
|
14045
14053
|
thread_target: string2().optional(),
|
|
14046
14054
|
author: object({ kind: string2(), id: string2() }).optional(),
|
|
14055
|
+
reply_block: ReplyBlockSchema.nullish(),
|
|
14047
14056
|
thread_logins: array(string2()).optional()
|
|
14048
14057
|
});
|
|
14049
14058
|
var DispatchTargetedResourceIDSchema = uuid2();
|
|
@@ -14598,7 +14607,7 @@ var dispatchToolSpecs = [
|
|
|
14598
14607
|
quote: z.string().describe("Optional exact quoted document text.").optional(),
|
|
14599
14608
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional(),
|
|
14600
14609
|
body: z.string({ max: DISPATCH_BODY_MAX }).describe(`Review comment, at most ${DISPATCH_BODY_MAX} characters.`),
|
|
14601
|
-
reply_to: z.string().describe("A comment id (uuid); replying to any comment in a thread continues that thread (an " + "ask's clarification thread included). To reply to an ask, use reply_to_ask with the " + "ask id instead.").optional(),
|
|
14610
|
+
reply_to: z.string().describe("A comment id (uuid); replying to any comment in a thread continues that thread (an " + "ask's clarification thread included). A block reference to one block of the comment " + "(dispatch://KEY/comment/<id>#b-<block id>) replies to that comment and names the " + "passage the reply answers. To reply to an ask, use reply_to_ask with the " + "ask id instead.").optional(),
|
|
14602
14611
|
reply_to_ask: z.string().describe("Optional ask id to reply to, threading this comment under that question. Mutually " + "exclusive with reply_to.").optional(),
|
|
14603
14612
|
turn: z.enum(["agent", "human"]).describe("Only with reply_to_ask: who holds the turn after this reply. agent: a progress note - " + "you keep the turn and the ask stays 'Waiting on agents' for the human; human (default): " + "you need the human to act - the ask returns to 'Waiting on you'.").optional(),
|
|
14604
14613
|
images: imagesArgument(z, "the issue (or the project document's project)", `the ${DISPATCH_BODY_MAX}-character body limit`)
|
|
@@ -14633,7 +14642,7 @@ var dispatchToolSpecs = [
|
|
|
14633
14642
|
arguments: (z) => ({
|
|
14634
14643
|
issue: z.string().describe(`${ISSUE_REFERENCE} Omit it only when in_reply_to answers a human's direct message to this session.`).optional(),
|
|
14635
14644
|
body: z.string({ max: DISPATCH_BODY_MAX }).describe(`Update text, at most ${DISPATCH_BODY_MAX} characters.`),
|
|
14636
|
-
in_reply_to: z.string().describe("Optional message id or dispatch://KEY/message/<id> reference to reply to, threading " + "this message under it so the reply stays with the original in the Conversation. " + "A bare id with no issue answers a human's direct message to this session; a " + "dispatch://KEY/message/<id> names the issue its message lives on, so that form is a " + "reply on that issue.").optional(),
|
|
14645
|
+
in_reply_to: z.string().describe("Optional message id or dispatch://KEY/message/<id> reference to reply to, threading " + "this message under it so the reply stays with the original in the Conversation. " + "A block reference to one block of the message (dispatch://KEY/message/<id>#b-<block id>) " + "replies to that message and names the passage the reply answers. " + "A bare id with no issue answers a human's direct message to this session; a " + "dispatch://KEY/message/<id> names the issue its message lives on, so that form is a " + "reply on that issue.").optional(),
|
|
14637
14646
|
images: imagesArgument(z, "the issue (a reply to a direct message: your own conversation, dispatch://agent/<your session id>)", `the ${DISPATCH_BODY_MAX}-character body limit`)
|
|
14638
14647
|
}),
|
|
14639
14648
|
validation: messageValidation
|
|
@@ -16117,6 +16126,12 @@ class DispatchClient {
|
|
|
16117
16126
|
async artifactBlocks(id) {
|
|
16118
16127
|
return this.#json("GET", ["api", "v1", "artifacts", id, "blocks"]);
|
|
16119
16128
|
}
|
|
16129
|
+
async commentBlocks(id) {
|
|
16130
|
+
return this.#json("GET", ["api", "v1", "comments", id, "blocks"]);
|
|
16131
|
+
}
|
|
16132
|
+
async messageBlocks(id, session) {
|
|
16133
|
+
return this.#json("GET", ["api", "v1", "messages", id, "blocks"], undefined, session === undefined ? undefined : { session });
|
|
16134
|
+
}
|
|
16120
16135
|
async docEdit(id, input) {
|
|
16121
16136
|
return this.#json("POST", ["api", "v1", "artifacts", id, "edits"], input);
|
|
16122
16137
|
}
|
|
@@ -16417,10 +16432,26 @@ function dispatchDocumentRef(owner, slug) {
|
|
|
16417
16432
|
function dispatchChildRef(ownerRef, kind, id) {
|
|
16418
16433
|
return `${ownerRef}/${kind}/${id}`;
|
|
16419
16434
|
}
|
|
16420
|
-
function parseBlockRef(
|
|
16421
|
-
const target = parseBlockFragment(fragment);
|
|
16422
|
-
const parsed = parseDispatchRef(
|
|
16423
|
-
if (target === undefined || parsed === null
|
|
16435
|
+
function parseBlockRef(holder, fragment) {
|
|
16436
|
+
const target = holder.includes("@") ? undefined : parseBlockFragment(fragment);
|
|
16437
|
+
const parsed = parseDispatchRef(holder);
|
|
16438
|
+
if (target === undefined || parsed === null) {
|
|
16439
|
+
return null;
|
|
16440
|
+
}
|
|
16441
|
+
const pin = target.pin === undefined ? {} : { pin: target.pin };
|
|
16442
|
+
const artifact = parsed.artifact === undefined ? {} : { artifact: parsed.artifact };
|
|
16443
|
+
if (parsed.kind === "comment" || parsed.kind === "message" && target.pin === undefined) {
|
|
16444
|
+
return {
|
|
16445
|
+
owner: parsed.owner,
|
|
16446
|
+
kind: "block",
|
|
16447
|
+
id: parsed.id,
|
|
16448
|
+
block: target.block,
|
|
16449
|
+
item: { kind: parsed.kind, id: parsed.id },
|
|
16450
|
+
...artifact,
|
|
16451
|
+
...pin
|
|
16452
|
+
};
|
|
16453
|
+
}
|
|
16454
|
+
if (parsed.kind !== "spec" && parsed.kind !== "artifact") {
|
|
16424
16455
|
return null;
|
|
16425
16456
|
}
|
|
16426
16457
|
return {
|
|
@@ -16428,9 +16459,9 @@ function parseBlockRef(document, fragment) {
|
|
|
16428
16459
|
kind: "block",
|
|
16429
16460
|
id: parsed.id,
|
|
16430
16461
|
block: target.block,
|
|
16431
|
-
document,
|
|
16462
|
+
document: holder,
|
|
16432
16463
|
...parsed.owner.kind === "project" ? { artifact: parsed.id } : {},
|
|
16433
|
-
...
|
|
16464
|
+
...pin
|
|
16434
16465
|
};
|
|
16435
16466
|
}
|
|
16436
16467
|
function parseDispatchRef(ref) {
|
|
@@ -17457,7 +17488,7 @@ function normalizeUUID(value) {
|
|
|
17457
17488
|
}
|
|
17458
17489
|
var askIdProblem = "ask must be a bare ask id or a dispatch://.../ask/<id> reference";
|
|
17459
17490
|
var commentIdProblem = "comment must be a bare comment id or a dispatch://.../comment/<id> reference";
|
|
17460
|
-
var messageIdProblem = "in_reply_to must be a full message id (uuid) or a dispatch://KEY/message/<id>
|
|
17491
|
+
var messageIdProblem = "in_reply_to must be a full message id (uuid), a dispatch://KEY/message/<id> reference, or a block of one (dispatch://KEY/message/<id>#b-<block id>)";
|
|
17461
17492
|
function askId(args) {
|
|
17462
17493
|
const ask = stringArg(args, "ask");
|
|
17463
17494
|
return ask.startsWith("dispatch://") ? parseDispatchRef(ask)?.id ?? ask : ask;
|
|
@@ -17469,6 +17500,28 @@ function messageIdOf(value) {
|
|
|
17469
17500
|
})() : value;
|
|
17470
17501
|
return id === undefined ? undefined : normalizeUUID(id);
|
|
17471
17502
|
}
|
|
17503
|
+
function itemBlockOf(value) {
|
|
17504
|
+
const ref = parseDispatchRef(value);
|
|
17505
|
+
return ref?.item === undefined || ref.block === undefined ? undefined : { ...ref, item: ref.item, block: ref.block };
|
|
17506
|
+
}
|
|
17507
|
+
function replyBlockProblem(field, kind, value) {
|
|
17508
|
+
if (!/^dispatch:\/\/[^#]*#b-/.test(value))
|
|
17509
|
+
return;
|
|
17510
|
+
const form = `dispatch://KEY/${kind}/<id>#b-<block id>`;
|
|
17511
|
+
const ref = parseDispatchRef(value);
|
|
17512
|
+
if (ref?.kind !== "block") {
|
|
17513
|
+
return `${field} ${value} names no block; a block of a ${kind} reads ${form}`;
|
|
17514
|
+
}
|
|
17515
|
+
const other = ref.item?.kind;
|
|
17516
|
+
if (other !== kind) {
|
|
17517
|
+
const answer = other === "comment" ? `; answer it with ${commandLine("dispatch_comment", { reply_to: value })}` : other === "message" ? `; answer it with ${commandLine("dispatch_message", { in_reply_to: value })}` : "";
|
|
17518
|
+
return `${field} ${value} names a block of ${other === undefined ? "a document" : `a ${other}`}, but a ${kind} reply answers a block of a ${kind} (${form})${answer}`;
|
|
17519
|
+
}
|
|
17520
|
+
if (ref.pin !== undefined) {
|
|
17521
|
+
return `${field} ${value} names revision ${ref.pin} of the block, but a reply answers the block whatever its text becomes: drop @v${ref.pin}`;
|
|
17522
|
+
}
|
|
17523
|
+
return;
|
|
17524
|
+
}
|
|
17472
17525
|
var idPrefixPattern = /^[0-9a-f][0-9a-f-]{7,}$/i;
|
|
17473
17526
|
function refOwnerName(ref) {
|
|
17474
17527
|
return ref.owner.kind === "issue" ? ref.owner.issue : `${ref.owner.project}/${ref.artifact ?? ref.id}`;
|
|
@@ -17578,7 +17631,20 @@ async function resolveAskArgument(tool, args, client, sessionId) {
|
|
|
17578
17631
|
}
|
|
17579
17632
|
function messageInReplyTo(args) {
|
|
17580
17633
|
const inReplyTo = optionalString(args, "in_reply_to");
|
|
17581
|
-
|
|
17634
|
+
if (inReplyTo === undefined)
|
|
17635
|
+
return;
|
|
17636
|
+
const block = itemBlockOf(inReplyTo);
|
|
17637
|
+
if (block !== undefined && block.item.kind !== "message")
|
|
17638
|
+
return;
|
|
17639
|
+
const id = messageIdOf(block?.item.id ?? inReplyTo);
|
|
17640
|
+
return id === undefined ? undefined : { id, ...block === undefined ? {} : { block: block.block } };
|
|
17641
|
+
}
|
|
17642
|
+
function commentReplyTo(args) {
|
|
17643
|
+
const replyTo = optionalString(args, "reply_to");
|
|
17644
|
+
if (replyTo === undefined)
|
|
17645
|
+
return;
|
|
17646
|
+
const block = itemBlockOf(replyTo);
|
|
17647
|
+
return block === undefined ? { id: replyTo } : { id: block.item.id, block: block.block };
|
|
17582
17648
|
}
|
|
17583
17649
|
function argumentProblems(tool, args) {
|
|
17584
17650
|
const problems = [];
|
|
@@ -17613,15 +17679,21 @@ function argumentProblems(tool, args) {
|
|
|
17613
17679
|
if (optionalString(args, "quote") !== undefined && optionalString(args, "artifact") === undefined) {
|
|
17614
17680
|
problems.push("artifact is required when quote is supplied");
|
|
17615
17681
|
}
|
|
17616
|
-
|
|
17682
|
+
const replyTo = optionalString(args, "reply_to");
|
|
17683
|
+
if (replyTo !== undefined && optionalString(args, "reply_to_ask") !== undefined) {
|
|
17617
17684
|
problems.push("reply_to and reply_to_ask cannot both be set");
|
|
17618
17685
|
}
|
|
17686
|
+
const replyBlock = replyTo === undefined ? undefined : replyBlockProblem("reply_to", "comment", replyTo);
|
|
17687
|
+
if (replyBlock !== undefined)
|
|
17688
|
+
problems.push(replyBlock);
|
|
17619
17689
|
break;
|
|
17620
17690
|
}
|
|
17621
17691
|
case "dispatch_message": {
|
|
17622
17692
|
const inReplyTo = optionalString(args, "in_reply_to");
|
|
17623
|
-
if (inReplyTo !== undefined
|
|
17624
|
-
|
|
17693
|
+
if (inReplyTo !== undefined) {
|
|
17694
|
+
const problem = replyBlockProblem("in_reply_to", "message", inReplyTo) ?? (messageInReplyTo(args) === undefined ? messageIdProblem : undefined);
|
|
17695
|
+
if (problem !== undefined)
|
|
17696
|
+
problems.push(problem);
|
|
17625
17697
|
}
|
|
17626
17698
|
break;
|
|
17627
17699
|
}
|
|
@@ -17701,7 +17773,7 @@ function dispatchRefFromUrl(value, serverUrl) {
|
|
|
17701
17773
|
};
|
|
17702
17774
|
const blockOf = (versionName) => {
|
|
17703
17775
|
const id = url.hash.startsWith("#b-") ? decodeBlockId(url.hash.slice(3)) : undefined;
|
|
17704
|
-
return id === undefined || url.searchParams.get(versionName) ? "" : `#b-${encodeBlockId(id)}`;
|
|
17776
|
+
return id === undefined || versionName !== undefined && url.searchParams.get(versionName) ? "" : `#b-${encodeBlockId(id)}`;
|
|
17705
17777
|
};
|
|
17706
17778
|
const issuePage = url.pathname.match(/^\/issues\/([A-Z][A-Z0-9]{1,9}-[1-9][0-9]*)(?:\/(spec|log|conversation|activity|children)|\/artifacts\/([^/]+)|\/asks\/([^/]+)|\/comments\/([^/]+)|\/messages\/([^/]+))?\/?$/);
|
|
17707
17779
|
if (issuePage) {
|
|
@@ -17727,9 +17799,9 @@ function dispatchRefFromUrl(value, serverUrl) {
|
|
|
17727
17799
|
if (ask !== undefined)
|
|
17728
17800
|
return `dispatch://${key}/ask/${ask}`;
|
|
17729
17801
|
if (comment !== undefined)
|
|
17730
|
-
return `dispatch://${key}/comment/${comment}`;
|
|
17802
|
+
return `dispatch://${key}/comment/${comment}${blockOf()}`;
|
|
17731
17803
|
if (message !== undefined)
|
|
17732
|
-
return `dispatch://${key}/message/${message}`;
|
|
17804
|
+
return `dispatch://${key}/message/${message}${blockOf()}`;
|
|
17733
17805
|
return `dispatch://${key}`;
|
|
17734
17806
|
}
|
|
17735
17807
|
const agentArtifactPage = url.pathname.match(/^\/agents\/([^/]+)\/artifacts\/([^/]+)\/?$/);
|
|
@@ -17752,7 +17824,7 @@ function dispatchRefFromUrl(value, serverUrl) {
|
|
|
17752
17824
|
return;
|
|
17753
17825
|
return item === undefined ? `${document}${versionOf("version")}${blockOf("version")}` : `${document}/${item.kind}/${item.id}`;
|
|
17754
17826
|
}
|
|
17755
|
-
var refGrammarProblem = "ref must be a valid dispatch:// reference such as dispatch://KEY-1, " + "dispatch://KEY-1/ask/<uuid>, dispatch://KEY-1/comment/<uuid>, " + "dispatch://KEY-1/message/<uuid>, dispatch://KEY-1/artifact/<slug>, " + "dispatch://PROJECT/artifact/<document-ref> (an artifact id, slug, or filename), " + "dispatch://KEY-1/spec#b-<block id>, dispatch://KEY-1/artifact/<slug>#b-<block id> or " + "dispatch://PROJECT/artifact/<slug>#b-<block id> (one block of a document, its id " + "percent-encoded, optionally pinned to a version of the document with @v<N> after it; for " + "dispatch read), or " + "dispatch://agent/<session id>/artifact/<slug> (a picture in a conversation on the Agents page, " + "for dispatch doc-read); a dashboard URL on this Dispatch server is accepted too";
|
|
17827
|
+
var refGrammarProblem = "ref must be a valid dispatch:// reference such as dispatch://KEY-1, " + "dispatch://KEY-1/ask/<uuid>, dispatch://KEY-1/comment/<uuid>, " + "dispatch://KEY-1/message/<uuid>, dispatch://KEY-1/artifact/<slug>, " + "dispatch://PROJECT/artifact/<document-ref> (an artifact id, slug, or filename), " + "dispatch://KEY-1/spec#b-<block id>, dispatch://KEY-1/artifact/<slug>#b-<block id> or " + "dispatch://PROJECT/artifact/<slug>#b-<block id> (one block of a document, its id " + "percent-encoded, optionally pinned to a version of the document with @v<N> after it; for " + "dispatch read), dispatch://KEY-1/comment/<uuid>#b-<block id>, " + "dispatch://PROJECT/artifact/<slug>/comment/<uuid>#b-<block id> or " + "dispatch://KEY-1/message/<uuid>#b-<block id> (one block of a comment or message, a " + "comment's optionally pinned to a revision of the comment with @v<N> after it; for dispatch " + "read), or " + "dispatch://agent/<session id>/artifact/<slug> (a picture in a conversation on the Agents page, " + "for dispatch doc-read); a dashboard URL on this Dispatch server is accepted too";
|
|
17756
17828
|
var ownerRequiredProblem = "issue is required; supply issue or set LEGION_ISSUE";
|
|
17757
17829
|
function innerRefProblem(tool, ref, refText) {
|
|
17758
17830
|
if (ref.kind !== "ask" && ref.kind !== "comment" && ref.kind !== "message" && ref.kind !== "log" && ref.kind !== "children" && ref.kind !== "block") {
|
|
@@ -17761,10 +17833,11 @@ function innerRefProblem(tool, ref, refText) {
|
|
|
17761
17833
|
if (tool === "dispatch_read" || tool === "dispatch_ask")
|
|
17762
17834
|
return;
|
|
17763
17835
|
const owner = refOwnerName(ref);
|
|
17764
|
-
const
|
|
17836
|
+
const itemKind = ref.item?.kind ?? ref.kind;
|
|
17837
|
+
const documentArgument = ref.kind === "block" && ref.item === undefined && tool !== "dispatch_comment" ? ` and artifact ${ref.id}` : tool === "dispatch_suggest" ? " and artifact <the document's slug>" : "";
|
|
17765
17838
|
const ownerArguments = ref.owner.kind === "issue" ? `issue ${ref.owner.issue}${documentArgument}` : `project ${ref.owner.project} and artifact ${ref.artifact}`;
|
|
17766
|
-
const fix = tool === "dispatch_comment" &&
|
|
17767
|
-
const item = ref.kind === "ask" ? "an ask" : ref.kind === "log" ? "the event log" : ref.kind === "children" ? "the children" : ref.kind === "block" ? `block ${ref.block}` : `a ${ref.kind}`;
|
|
17839
|
+
const fix = tool === "dispatch_comment" && itemKind === "ask" ? `reply under it with reply_to_ask ${ref.id} and ${ownerArguments}` : tool === "dispatch_comment" && itemKind === "comment" ? `reply in its thread with reply_to ${ref.id} and ${ownerArguments}` : `read it with dispatch read, or name ${owner} with ${ownerArguments}`;
|
|
17840
|
+
const item = ref.kind === "ask" ? "an ask" : ref.kind === "log" ? "the event log" : ref.kind === "children" ? "the children" : ref.kind === "block" ? `block ${ref.block}${ref.item === undefined ? "" : ` of a ${ref.item.kind}`}` : `a ${ref.kind}`;
|
|
17768
17841
|
return `ref ${refText} names ${item} on ${owner}; dispatch ${commandName(tool)} would act on ${owner} and drop the ${ref.kind}: ${fix}`;
|
|
17769
17842
|
}
|
|
17770
17843
|
var documentItemPattern = /^dispatch:\/\/([A-Z][A-Z0-9]{1,9}-[1-9][0-9]*)\/artifact\/[^/@]+(?:@v\d+)?\/(ask|comment)\/([^/]+)$/;
|
|
@@ -17791,7 +17864,7 @@ function ownerConflictProblems(tool, args, parsedRef, refText) {
|
|
|
17791
17864
|
const artifact = optionalString(args, "artifact");
|
|
17792
17865
|
const version = optionalNumber(args, "version");
|
|
17793
17866
|
const ref = tool === "dispatch_ask" && (issue !== undefined || project !== undefined) ? null : parsedRef;
|
|
17794
|
-
const refDocument = ref === null ? undefined : ref.owner.kind === "project" ? ref.artifact ?? ref.id : ref.kind === "spec" || ref.kind === "artifact" || ref.kind === "block" ? ref.id : undefined;
|
|
17867
|
+
const refDocument = ref === null ? undefined : ref.owner.kind === "project" ? ref.artifact ?? ref.id : ref.kind === "spec" || ref.kind === "artifact" || ref.kind === "block" && ref.item === undefined ? ref.id : undefined;
|
|
17795
17868
|
if (ref !== null && refText !== undefined) {
|
|
17796
17869
|
const refOwner = refOwnerName(ref);
|
|
17797
17870
|
if (issue !== undefined && (ref.owner.kind !== "issue" || nativeIssueKeyPattern.test(issue) && issue !== ref.owner.issue)) {
|
|
@@ -18198,9 +18271,12 @@ function referenceLines(read) {
|
|
|
18198
18271
|
if (read.edges.length === 0)
|
|
18199
18272
|
return ["- none"];
|
|
18200
18273
|
return read.edges.map((edge) => {
|
|
18274
|
+
const { node } = edge;
|
|
18201
18275
|
const excerpt = edge.excerpt === undefined ? "" : `${textHead(edge.excerpt.text)} \xB7 `;
|
|
18202
18276
|
const block = edge.block === undefined ? "" : ` (block ${edge.block.ref ?? edge.block.id})`;
|
|
18203
|
-
|
|
18277
|
+
const item = node.kind === "block" && node.document === undefined && (node.parent?.kind === "comment" || node.parent?.kind === "message") ? node.parent : undefined;
|
|
18278
|
+
const source = item === undefined ? `${node.kind} ${node.ref ?? node.id}` : `${item.kind} ${item.ref ?? item.id} block ${node.block_id ?? node.id}`;
|
|
18279
|
+
return `- ${edge.kind} ${source} (${excerpt}${edge.created_at})${block}`;
|
|
18204
18280
|
});
|
|
18205
18281
|
}
|
|
18206
18282
|
function unavailableReason(error) {
|
|
@@ -18243,27 +18319,32 @@ function nodeLine(node) {
|
|
|
18243
18319
|
const made = fact(node.made_by, node.made_at);
|
|
18244
18320
|
const changed = fact(node.changed_by, node.changed_at);
|
|
18245
18321
|
const { parent } = node;
|
|
18246
|
-
const partOf = parent == null ? "" : ` \xB7 part of ${parent.ref ?? `${parent.kind} ${parent.id}`}${node.parent_version == null ? "" : ` (version ${node.parent_version})`}`;
|
|
18322
|
+
const partOf = parent == null ? "" : ` \xB7 part of ${parent.ref ?? `${parent.kind} ${parent.id}`}${node.parent_version == null ? "" : ` (${parent.kind === "comment" ? "revision" : "version"} ${node.parent_version})`}`;
|
|
18247
18323
|
return [
|
|
18248
18324
|
`Node: ${node.kind} ${node.ref ?? node.id} \xB7 made by ${made} \xB7 changed by ${changed}${partOf}`
|
|
18249
18325
|
];
|
|
18250
18326
|
}
|
|
18251
18327
|
function blockLines(node, ref) {
|
|
18252
|
-
const
|
|
18253
|
-
|
|
18328
|
+
const unit = ref.item === undefined ? "version" : "revision";
|
|
18329
|
+
const blockID = node.block_id ?? ref.block;
|
|
18330
|
+
const head = ref.item === undefined ? [
|
|
18331
|
+
`Block: ${blockID}`,
|
|
18254
18332
|
`Document: ${node.document ?? ref.document}`,
|
|
18255
18333
|
...node.changed_version === undefined ? [] : [`Changed in version: ${node.changed_version}`],
|
|
18256
18334
|
...node.text === undefined ? [] : ["Text:", node.text]
|
|
18335
|
+
] : [
|
|
18336
|
+
`Block ${blockID} of ${ref.item.kind} ${ref.item.id}${node.changed_version === undefined ? "" : ` (changed in revision ${node.changed_version})`}:`,
|
|
18337
|
+
...node.text === undefined ? [] : [node.text]
|
|
18257
18338
|
];
|
|
18258
18339
|
const { pin } = node;
|
|
18259
18340
|
if (pin === undefined)
|
|
18260
18341
|
return head;
|
|
18261
|
-
const state = pin.state === "unchanged" ? `Unchanged since
|
|
18342
|
+
const state = pin.state === "unchanged" ? `Unchanged since ${unit} ${pin.version}.` : pin.state === "changed" ? `Changed since ${unit} ${pin.version}; last changed in ${unit} ${node.changed_version}.` : `Removed in ${unit} ${pin.removed_version}.`;
|
|
18262
18343
|
return [
|
|
18263
18344
|
...head,
|
|
18264
|
-
`Pinned to
|
|
18345
|
+
`Pinned to ${unit}: ${pin.version}`,
|
|
18265
18346
|
state,
|
|
18266
|
-
`Text at
|
|
18347
|
+
`Text at ${unit} ${pin.version}:`,
|
|
18267
18348
|
pin.text
|
|
18268
18349
|
];
|
|
18269
18350
|
}
|
|
@@ -18410,11 +18491,13 @@ function commentSummary({ comment, replies }, graph) {
|
|
|
18410
18491
|
const root = [
|
|
18411
18492
|
`${comment.id} \xB7 ${actorText(comment.author)}`,
|
|
18412
18493
|
...anchorLines(comment),
|
|
18494
|
+
...replyBlockLines(comment),
|
|
18413
18495
|
`Body: ${comment.body}`
|
|
18414
18496
|
];
|
|
18415
18497
|
const chain = replies.flatMap((reply) => [
|
|
18416
18498
|
`${reply.id} \xB7 ${actorText(reply.author)}`,
|
|
18417
18499
|
...anchorLines(reply),
|
|
18500
|
+
...replyBlockLines(reply),
|
|
18418
18501
|
`Body: ${reply.body}`
|
|
18419
18502
|
]);
|
|
18420
18503
|
return [
|
|
@@ -18426,6 +18509,14 @@ function commentSummary({ comment, replies }, graph) {
|
|
|
18426
18509
|
].join(`
|
|
18427
18510
|
`);
|
|
18428
18511
|
}
|
|
18512
|
+
function replyBlockLines(record) {
|
|
18513
|
+
const block = record.reply_block;
|
|
18514
|
+
if (block === undefined || block === null)
|
|
18515
|
+
return [];
|
|
18516
|
+
return [
|
|
18517
|
+
block.removed ? `Re: block ${block.block_id} (removed)` : `Re: block ${block.block_id}: ${textHead(block.text)}`
|
|
18518
|
+
];
|
|
18519
|
+
}
|
|
18429
18520
|
function anchorLines(record) {
|
|
18430
18521
|
return [
|
|
18431
18522
|
...record.anchor?.quote === undefined ? [] : [`> ${record.anchor.quote}`],
|
|
@@ -18449,9 +18540,14 @@ function positionText(block) {
|
|
|
18449
18540
|
].join(" \u203A ");
|
|
18450
18541
|
}
|
|
18451
18542
|
function messageSummary({ message, replies }, graph) {
|
|
18452
|
-
const root = [
|
|
18543
|
+
const root = [
|
|
18544
|
+
`${message.id} \xB7 ${actorText(message.author)}`,
|
|
18545
|
+
...replyBlockLines(message),
|
|
18546
|
+
`Body: ${message.body}`
|
|
18547
|
+
];
|
|
18453
18548
|
const chain = replies.flatMap((reply) => [
|
|
18454
18549
|
`${reply.id} \xB7 ${actorText(reply.author)}`,
|
|
18550
|
+
...replyBlockLines(reply),
|
|
18455
18551
|
`Body: ${reply.body}`
|
|
18456
18552
|
]);
|
|
18457
18553
|
return [
|
|
@@ -19309,7 +19405,7 @@ async function executeDispatchTool(input) {
|
|
|
19309
19405
|
const owner = documentOwner();
|
|
19310
19406
|
const resolved = owner.kind === "project" || artifactReference === undefined ? owner.kind === "project" ? await resolveDocument(owner, artifactReference) : undefined : await resolveDocument(owner, artifactReference);
|
|
19311
19407
|
const anchored = resolved ? anchor(resolved.artifact, args) : undefined;
|
|
19312
|
-
const replyTo =
|
|
19408
|
+
const replyTo = commentReplyTo(args);
|
|
19313
19409
|
const replyToAskReference = optionalString(args, "reply_to_ask");
|
|
19314
19410
|
const replyToAsk = replyToAskReference === undefined ? undefined : normalizeUUID(replyToAskReference);
|
|
19315
19411
|
if (replyToAskReference !== undefined && replyToAsk === undefined) {
|
|
@@ -19326,7 +19422,8 @@ async function executeDispatchTool(input) {
|
|
|
19326
19422
|
const commentInput = {
|
|
19327
19423
|
body,
|
|
19328
19424
|
...anchored === undefined ? {} : { anchor: anchored },
|
|
19329
|
-
...replyTo === undefined ? {} : { reply_to: replyTo },
|
|
19425
|
+
...replyTo === undefined ? {} : { reply_to: replyTo.id },
|
|
19426
|
+
...replyTo?.block === undefined ? {} : { reply_to_block: replyTo.block },
|
|
19330
19427
|
...replyToAsk === undefined ? {} : { ask_id: replyToAsk },
|
|
19331
19428
|
...requestedTurn === undefined ? {} : { turn: requestedTurn },
|
|
19332
19429
|
actor
|
|
@@ -19386,7 +19483,8 @@ async function executeDispatchTool(input) {
|
|
|
19386
19483
|
};
|
|
19387
19484
|
}
|
|
19388
19485
|
case "dispatch_message": {
|
|
19389
|
-
const
|
|
19486
|
+
const target = messageInReplyTo(args);
|
|
19487
|
+
const inReplyTo = target?.id;
|
|
19390
19488
|
const written = stringArg(args, "body");
|
|
19391
19489
|
const bodyLimit = {
|
|
19392
19490
|
cap: DISPATCH_BODY_MAX,
|
|
@@ -19433,6 +19531,7 @@ async function executeDispatchTool(input) {
|
|
|
19433
19531
|
const message = await client.message(issueKey, {
|
|
19434
19532
|
body,
|
|
19435
19533
|
...inReplyTo === undefined ? {} : { in_reply_to: inReplyTo },
|
|
19534
|
+
...target?.block === undefined ? {} : { in_reply_to_block: target.block },
|
|
19436
19535
|
actor
|
|
19437
19536
|
});
|
|
19438
19537
|
const messageRef = dispatchChildRef(dispatchIssueRef(issueKey), "message", message.id);
|
|
@@ -19727,10 +19826,11 @@ Document token: ${listed.token}`,
|
|
|
19727
19826
|
if (ownerArguments.ref?.kind === "block") {
|
|
19728
19827
|
const ref = ownerArguments.ref;
|
|
19729
19828
|
const blockRef = stringArg(args, "ref");
|
|
19829
|
+
const holder = blockRef.slice(0, blockRef.indexOf("#"));
|
|
19730
19830
|
const [incoming, outgoing] = await Promise.all([
|
|
19731
19831
|
client.getReferences({ to: blockRef }).catch((error) => {
|
|
19732
19832
|
if (ref.pin === undefined && dispatchAnswered(error, 404)) {
|
|
19733
|
-
throw new Error(`No block ${ref.block} in ${
|
|
19833
|
+
throw new Error(`No block ${ref.block} in ${holder}.`);
|
|
19734
19834
|
}
|
|
19735
19835
|
throw error;
|
|
19736
19836
|
}),
|
package/dist/src/server.js
CHANGED
|
@@ -13698,6 +13698,13 @@ var CommentDeliverySchema = object({
|
|
|
13698
13698
|
reply_id: string2().nullable(),
|
|
13699
13699
|
created_at: string2()
|
|
13700
13700
|
});
|
|
13701
|
+
var ReplyBlockSchema = object({
|
|
13702
|
+
node: string2(),
|
|
13703
|
+
ref: string2().optional(),
|
|
13704
|
+
block_id: string2(),
|
|
13705
|
+
text: string2(),
|
|
13706
|
+
removed: boolean2()
|
|
13707
|
+
});
|
|
13701
13708
|
var CommentEventPayloadSchema = object({
|
|
13702
13709
|
id: string2().optional(),
|
|
13703
13710
|
artifact_name: string2().optional(),
|
|
@@ -13723,6 +13730,7 @@ var CommentEventPayloadSchema = object({
|
|
|
13723
13730
|
created_at: string2().optional(),
|
|
13724
13731
|
mentions: array(CommentMentionSchema).optional(),
|
|
13725
13732
|
deliveries: array(CommentDeliverySchema).optional(),
|
|
13733
|
+
reply_block: ReplyBlockSchema.nullish(),
|
|
13726
13734
|
thread_logins: array(string2()).optional()
|
|
13727
13735
|
}).passthrough();
|
|
13728
13736
|
var MessageEventPayloadSchema = object({
|
|
@@ -13733,6 +13741,7 @@ var MessageEventPayloadSchema = object({
|
|
|
13733
13741
|
reply_body: string2().optional(),
|
|
13734
13742
|
thread_target: string2().optional(),
|
|
13735
13743
|
author: object({ kind: string2(), id: string2() }).optional(),
|
|
13744
|
+
reply_block: ReplyBlockSchema.nullish(),
|
|
13736
13745
|
thread_logins: array(string2()).optional()
|
|
13737
13746
|
});
|
|
13738
13747
|
var DispatchTargetedResourceIDSchema = uuid2();
|
|
@@ -14116,7 +14125,7 @@ var dispatchToolSpecs = [
|
|
|
14116
14125
|
quote: z.string().describe("Optional exact quoted document text.").optional(),
|
|
14117
14126
|
occurrence: z.number({ int: true, min: 0 }).describe("Optional zero-based occurrence of quote.").optional(),
|
|
14118
14127
|
body: z.string({ max: DISPATCH_BODY_MAX }).describe(`Review comment, at most ${DISPATCH_BODY_MAX} characters.`),
|
|
14119
|
-
reply_to: z.string().describe("A comment id (uuid); replying to any comment in a thread continues that thread (an " + "ask's clarification thread included). To reply to an ask, use reply_to_ask with the " + "ask id instead.").optional(),
|
|
14128
|
+
reply_to: z.string().describe("A comment id (uuid); replying to any comment in a thread continues that thread (an " + "ask's clarification thread included). A block reference to one block of the comment " + "(dispatch://KEY/comment/<id>#b-<block id>) replies to that comment and names the " + "passage the reply answers. To reply to an ask, use reply_to_ask with the " + "ask id instead.").optional(),
|
|
14120
14129
|
reply_to_ask: z.string().describe("Optional ask id to reply to, threading this comment under that question. Mutually " + "exclusive with reply_to.").optional(),
|
|
14121
14130
|
turn: z.enum(["agent", "human"]).describe("Only with reply_to_ask: who holds the turn after this reply. agent: a progress note - " + "you keep the turn and the ask stays 'Waiting on agents' for the human; human (default): " + "you need the human to act - the ask returns to 'Waiting on you'.").optional(),
|
|
14122
14131
|
images: imagesArgument(z, "the issue (or the project document's project)", `the ${DISPATCH_BODY_MAX}-character body limit`)
|
|
@@ -14151,7 +14160,7 @@ var dispatchToolSpecs = [
|
|
|
14151
14160
|
arguments: (z) => ({
|
|
14152
14161
|
issue: z.string().describe(`${ISSUE_REFERENCE} Omit it only when in_reply_to answers a human's direct message to this session.`).optional(),
|
|
14153
14162
|
body: z.string({ max: DISPATCH_BODY_MAX }).describe(`Update text, at most ${DISPATCH_BODY_MAX} characters.`),
|
|
14154
|
-
in_reply_to: z.string().describe("Optional message id or dispatch://KEY/message/<id> reference to reply to, threading " + "this message under it so the reply stays with the original in the Conversation. " + "A bare id with no issue answers a human's direct message to this session; a " + "dispatch://KEY/message/<id> names the issue its message lives on, so that form is a " + "reply on that issue.").optional(),
|
|
14163
|
+
in_reply_to: z.string().describe("Optional message id or dispatch://KEY/message/<id> reference to reply to, threading " + "this message under it so the reply stays with the original in the Conversation. " + "A block reference to one block of the message (dispatch://KEY/message/<id>#b-<block id>) " + "replies to that message and names the passage the reply answers. " + "A bare id with no issue answers a human's direct message to this session; a " + "dispatch://KEY/message/<id> names the issue its message lives on, so that form is a " + "reply on that issue.").optional(),
|
|
14155
14164
|
images: imagesArgument(z, "the issue (a reply to a direct message: your own conversation, dispatch://agent/<your session id>)", `the ${DISPATCH_BODY_MAX}-character body limit`)
|
|
14156
14165
|
}),
|
|
14157
14166
|
validation: messageValidation
|
|
@@ -15610,7 +15619,7 @@ function sourceSession(sessionID) {
|
|
|
15610
15619
|
// src/server.ts
|
|
15611
15620
|
import { tool } from "@opencode-ai/plugin/tool";
|
|
15612
15621
|
// package.json
|
|
15613
|
-
var version2 = "
|
|
15622
|
+
var version2 = "12.0.0";
|
|
15614
15623
|
|
|
15615
15624
|
// src/log.ts
|
|
15616
15625
|
import { appendFile, mkdir } from "fs/promises";
|
package/package.json
CHANGED
package/skills/dispatch/SKILL.md
CHANGED
|
@@ -10,7 +10,7 @@ Dispatch is a record for the people who decide, never a log of your work.
|
|
|
10
10
|
- Use the `dispatch` command; the HTTP API only for what no command does. `dispatch <command> --help` gives its rules and an example.
|
|
11
11
|
- Put long text on stdin: `--<field>-file -` with a quoted here-document (`<<'EOF'`). Give object and list fields as JSON.
|
|
12
12
|
- Exit 1 is a refusal, 2 a usage error. A refusal lists each problem: fix them all, then call once.
|
|
13
|
-
- `DOC_SERVICE_UNAVAILABLE`: the document is reloading, nothing was written; retry
|
|
13
|
+
- `DOC_SERVICE_UNAVAILABLE`: the document is reloading, nothing was written; retry shortly.
|
|
14
14
|
|
|
15
15
|
## Asks and ask blocks
|
|
16
16
|
|
|
@@ -27,7 +27,7 @@ Which machines, files or people, the numbers, then "A happens to X, or B to Y?"
|
|
|
27
27
|
|
|
28
28
|
- A to-do or permission only a person can give, or a decision with no document, is a `dispatch ask`.
|
|
29
29
|
- In Legion, fix a clear defect in code your tree uses, hand others to the controller, and ask about decisions (`skill://legion-worker/references/findings.md`).
|
|
30
|
-
- An open ask is the request. Do not
|
|
30
|
+
- An open ask is the request. Do not repeat it in each message.
|
|
31
31
|
- Change an open ask's block with `dispatch edit-ask`, or `doc-edit`'s `replace` or `move`. An answered ask is fixed: ask anew.
|
|
32
32
|
- Answer a person's reply to an open ask with `dispatch comment --reply-to-ask`; `--turn agent` for a note that needs nothing from them.
|
|
33
33
|
- When an answer's option and text disagree, the text wins.
|
|
@@ -72,9 +72,12 @@ dispatch://KEY/spec
|
|
|
72
72
|
dispatch://KEY/artifact/<slug>[@vN]
|
|
73
73
|
dispatch://KEY/ask/<id>
|
|
74
74
|
dispatch://KEY/comment/<id>
|
|
75
|
+
dispatch://KEY/comment/<id>#b-<block>[@vN]
|
|
75
76
|
dispatch://KEY/message/<id>
|
|
77
|
+
dispatch://KEY/message/<id>#b-<block>
|
|
76
78
|
dispatch://PROJECT/artifact/<slug>[@vN]
|
|
77
79
|
dispatch://PROJECT/artifact/<slug>/ask/<id>
|
|
80
|
+
dispatch://PROJECT/artifact/<slug>/comment/<id>#b-<block>[@vN]
|
|
78
81
|
```
|
|
79
82
|
|
|
80
83
|
A bare id or filename is not a reference. An ask or comment id can be an 8-hex prefix.
|
|
@@ -13,7 +13,7 @@ You own one issue tree from its first decision to the merge that closes it. The
|
|
|
13
13
|
|
|
14
14
|
- Act on delivered wakes and the live artifacts they name; build no wait loop, since every wake reaches you.
|
|
15
15
|
- Check each claim against the code as fetched now (the default branch your addressing line names, or the head under review), never the workspace as it was at admission, and tell every fresh reader you dispatch to do the same: the branch may have moved since.
|
|
16
|
-
- Before you end a turn that waits on a human, open what they must answer: a decision block, a `dispatch ask` or `dispatch request-approval`. A turn that ends with nothing open asks nobody, and the tree stops. Open an ordinary ask on the root issue itself, never a child: only a question a session of this tree opens on the root starts a wait on a person, so an ask on a child leaves the root holding its slot while nothing runs. Once you have handed a root question to the human, end your turn: when nothing else holds the slot — no other issue of the tree in a phase, the `legion` label still on, and no person or outside session holding the root's Dispatch claim — the root returns its admission slot (`awaiting_person`)
|
|
16
|
+
- Before you end a turn that waits on a human, open what they must answer: a decision block, a `dispatch ask` or `dispatch request-approval`. A turn that ends with nothing open asks nobody, and the tree stops. Open an ordinary ask on the root issue itself, never a child: only a question a session of this tree opens on the root starts a wait on a person, so an ask on a child leaves the root holding its slot while nothing runs. Once you have handed a root question to the human, end your turn: when nothing else holds the slot — no other issue of the tree in a phase, the `legion` label still on, and no person or outside session holding the root's Dispatch claim — the root returns its admission slot (`awaiting_person`) while this session stays live. Read the human's comments and replies as they arrive. Their answer gives this live claim a task naming what answered and what is still with them. The same holds once you request approval and `register_gate` that version: the registration hands the spec to the human, so end your turn there too, and the human's review, or a newer version once Dispatch shows the request back on your turn, gives you that task.
|
|
17
17
|
- Deliver every criterion of the root spec in this tree. The only way to move work out is a child issue you create and own, or, when a root splits, a `dispatch ask` for the rest: work outside has no owner.
|
|
18
18
|
- A problem outside the tree is a `dispatch ask`, never an issue you file, because a person decides whether it needs one (`skill://legion-worker/references/findings.md`). A clear defect is the exception. Fix one in code this tree touches or depends on in this tree's pull request. Send any other to the controller, which gives it to the tree that owns that code.
|
|
19
19
|
- Before you write a spec or ask a person anything, read the repository's `STRATEGY.md` when it has one, then use a standard best-practice solution; ask only what neither settles, with one recommendation. A design that crosses a Boundary in that file goes to its owner before it is built. Find the standard with a web search and with how two or three well-regarded projects solve the problem. Cite them in a few lines of the spec's `Design`. A design far from the standard is a sign to fix the underlying problem, not to add a workaround.
|
|
@@ -105,15 +105,15 @@ Facts about the request:
|
|
|
105
105
|
|
|
106
106
|
Then end your turn. The registration hands the spec to the human: when nothing else holds the
|
|
107
107
|
slot, the root returns its admission slot and waits on its design gate (`awaiting_person`), as a
|
|
108
|
-
root question handed to the human does ("Every turn" in `skill://legion-architect/SKILL.md`).
|
|
109
|
-
|
|
110
|
-
shows the request back on your turn. While the
|
|
111
|
-
approval or changes request of the spec answers that
|
|
112
|
-
spec stands. Release no wave until a delivered wake shows
|
|
113
|
-
|
|
114
|
-
`design-approved` notice, and "this resume is your design-approved wake" is
|
|
115
|
-
`design-changes-requested`: revise the spec as the human's reason asks, run step 3's
|
|
116
|
-
the new version, resolve every finding, request approval again and end your turn. A
|
|
108
|
+
root question handed to the human does ("Every turn" in `skill://legion-architect/SKILL.md`). Stay
|
|
109
|
+
live to read the human's comments and replies. Their approval or changes request delivers your next
|
|
110
|
+
task, and so does a newer spec version once Dispatch shows the request back on your turn. While the
|
|
111
|
+
root waits on a question instead, the human's approval or changes request of the spec answers that
|
|
112
|
+
wait too, and the task says how the spec stands. Release no wave until a delivered wake shows
|
|
113
|
+
`design-approved` on the root, or a task says its design gate is open: the daemon sends a root
|
|
114
|
+
waiting on a person no `design-approved` notice, and "this resume is your design-approved wake" is
|
|
115
|
+
that wake. On `design-changes-requested`: revise the spec as the human's reason asks, run step 3's
|
|
116
|
+
two reviews on the new version, resolve every finding, request approval again and end your turn. A
|
|
117
117
|
`design-changes-requested` may name a version below the latest, or arrive after another for the
|
|
118
118
|
same version: it is a request the human made that reached the daemon late, and no approval came
|
|
119
119
|
after it. Each request arrives once. The gate is as the newest review left it; still read the
|
|
@@ -8,7 +8,7 @@ waits, or neither. Read its key against `admission` and its tree's root.
|
|
|
8
8
|
|---|---|---|
|
|
9
9
|
| The key is in `admission.active` | A root holding a slot: its tree runs, `admitted` until its architect starts the first phase | Nothing, beyond [trees waiting on a root claim](skill://legion-controller/SKILL.md#trees-waiting-on-a-root-claim) |
|
|
10
10
|
| The key is in `admission.waiting` and not in `admission.blocked` | A root handed to Legion in `todo`, queued for the next free slot: a root a person answered while it waited in `awaiting_person` comes first, oldest answer first, then every root that never ran, in Dispatch rank order. A root that left another tree (handed over by its finished tree, or admitted as an orphan) waits here as its own root even under a live tree, the old root's return included: no tree adopts it | Nothing: it takes the next free slot by itself |
|
|
11
|
-
| `phase: awaiting_person`, `waitingOn` | A root that already ran and now waits only on a person: an answer to its architect's question (`waitingOn.kind` `ask`) or a review of its spec (`gate`). It holds no slot, its architect
|
|
11
|
+
| `phase: awaiting_person`, `waitingOn` | A root that already ran and now waits only on a person: an answer to its architect's question (`waitingOn.kind` `ask`) or a review of its spec (`gate`). It holds no slot, its architect stays live to read the person's comments and replies, and its tree stays live. Unanswered, it is in neither list; `waitingOn.answeredAt` means the daemon has promoted it to the front of `admission.waiting` | Nothing: it is not something to admit, label or park, and the slot it returned is no reason to take an issue ([Free slots](skill://legion-controller/SKILL.md#free-slots)). The daemon delivers the answer task to its architect once the root takes the next free slot. If its label goes or a person or outside session claims it on Dispatch, the daemon sends `unslotted` and does not resume work under another owner |
|
|
12
12
|
| The key is in `admission.waiting` and in `admission.blocked` | A root in the line that waits on the Dispatch issues `admission.blocked` names: admission passes it over, and it takes no slot, until every one of them is `done` | Nothing: the wait ends when each blocker reaches `done` (a person's close, or the blocker's own merge) or its link is taken off (by a person or the tree's architect). Name the wait in the day's report |
|
|
13
13
|
| `phase: admitted`, in neither list, and Dispatch shows a `child_of` parent | A child: it runs or waits with its tree, the root its `child_of` chain reaches. Under a root whose `phase` is `done` it waits with it. A labelled `todo` issue here is not in the line either: one whose recorded parent is in a live tree runs in that tree (the daemon adopted it there), or is held with that parent while the parent itself is held under an unreleased child, and one whose parent is not recorded but sits under a live tree is held under an unreleased child of that tree until its architect releases it or its parent. That hold is not `admission.blocked`, which names only blocked-by links | Leave it to its tree. It runs again when its root does, or as a root of its own once it is `todo` with the `legion` label and no live tree is above it |
|
|
14
14
|
| `phase: admitted`, `status: todo`, in neither list, no parent | A root a person took out of the line by taking its label off | Nothing: the label puts it back in the line, and a person's label is their decision |
|
|
@@ -63,13 +63,12 @@ wake gives it>; `todo` with the `legion` label queues it again". Other admission
|
|
|
63
63
|
## `awaiting-person on <KEY>` (`{kind: "awaiting-person", reason}`)
|
|
64
64
|
|
|
65
65
|
A root whose architect asked a person a question, or handed its spec to a person for approval, and
|
|
66
|
-
now waits only on them: the daemon returned
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
returned: it is no reason to label or admit an issue ([Free
|
|
66
|
+
now waits only on them: the daemon returned its slot while the architect stays live to read the
|
|
67
|
+
person's comments and replies. Read `issues.<KEY>.waitingOn`. Do not park it or start anything, and
|
|
68
|
+
take nothing for the slot it returned: it is no reason to label or admit an issue ([Free
|
|
70
69
|
slots](skill://legion-controller/SKILL.md#free-slots)). The person's answer puts it at the front of
|
|
71
|
-
`admission.waiting` by itself, and the next free slot
|
|
72
|
-
|
|
70
|
+
`admission.waiting` by itself, and the next free slot delivers its task. A lost label or a foreign
|
|
71
|
+
Dispatch claim sends the separate `unslotted` wake.
|
|
73
72
|
|
|
74
73
|
## `held on <KEY>` (`{kind: "held", phase, role?, reason?}`)
|
|
75
74
|
|