@sjawhar/pi-legion-envoy 5.5.0 → 5.5.2

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/envoy.js CHANGED
@@ -30240,7 +30240,7 @@ var dispatchToolSpecs = [
30240
30240
  ops: [{ op: "delete_column", block: "table-123", index: 1 }],
30241
30241
  precondition: { blocks: [{ id: "table-123", token: "sha256:current-table-token" }] }
30242
30242
  },
30243
- description: "Apply deterministic document edits: replace or delete quoted text, insert markdown at an anchor, retype an identified paragraph or typed block into a schema-declared typed block, delete or move a whole block by its id, or delete a table row or column in place. " + "Do not use it for review feedback or for reading; use dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. " + `For replace, delete, and quote anchors, find text as rendered: inline Markdown (**bold**, \`code\`) is tolerated and must be balanced; a leading '# ' matches a heading at any level. replace is inline: with is the new text of the matched span, so a marker of a different kind from the block's own stays literal text ('4. Design' written into a heading). A with that opens with a marker of the same kind as the matched block's own would write it twice and is INVALID_OP - including prose that merely looks like one ('1999. was a year' into an ordered item), which you write as text by escaping it ('1999\\. was a year'). The exception is a heading rename whose find carried a heading marker: replace(find="## Old", with="## New") gives '## New', and a different level applies only when find named the heading's actual level (find "## Old" with "### New" makes it an h3), since '# ' selects a heading without naming its level. Any non-empty with that renders to no text - a line indented four spaces or a tab, which markdown reads as a code block, or whitespace alone - is INVALID_OP rather than a silent deletion; pass an empty with to delete the matched text on purpose. ` + "with cannot open a new block: after a hard line break inside with (two trailing spaces, or a backslash, before the newline) a heading, bullet, '1.'/'1)' ordered, or '>' blockquote marker is INVALID_OP too, since that line would stay escaped text inside the matched block - use insert, plus delete for what it replaces, to add the block. " + "A delete whose find is a block's entire text removes the block (a list emptied of its items goes too); delete with block removes any block by id, and move with block relocates one. delete_row and delete_column take a table block and a zero-based index, preserving the table block id and refusing to remove cells with open asks or unresolved comments. " + 'Insert and move anchors also accept "start", "end", "heading:<exact heading text>", and "block:<id>"; block ids and their tokens come from GET /api/v1/artifacts/{artifact UUID}/blocks (the route takes the artifact UUID, not its slug). ' + "Optionally require the state just read: precondition selects exactly one of a document token from dispatch_doc_read, or block {id, token} values from /blocks. A block guard must include every block the batch changes; Dispatch resolves quote targets and rejects an uncovered batch rather than applying it. Use a document token for insert or move, which depend on document order. Prefer block tokens when the covered content blocks are independent sections. Tokens include inline marks, so a fresh human comment also makes a stale edit fail. PRECONDITION_FAILED means re-read; EDIT_QUEUE_FULL means back off before retrying. " + "The result carries the document token this edit produced, so a chain of guarded edits passes each result's token as the next edit's precondition with no dispatch_doc_read between them. " + "A batch that leaves the document exactly as it was mints no version, named or not, and the result says nothing changed and names each operation that did nothing. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
30243
+ description: "Apply deterministic document edits: replace or delete quoted text, insert markdown at an anchor, retype an identified paragraph or typed block into a schema-declared typed block, delete or move a whole block by its id, or delete a table row or column in place. " + "Do not use it for review feedback or for reading; use dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. " + `For replace, delete, and quote anchors, find text as rendered: inline Markdown (**bold**, \`code\`) is tolerated and must be balanced; a leading '# ' matches a heading at any level. replace is inline: with is the new text of the matched span, so a marker of a different kind from the block's own stays literal text ('4. Design' written into a heading). A with that opens with a marker of the same kind as the matched block's own would write it twice and is INVALID_OP - including prose that merely looks like one ('1999. was a year' into an ordered item), which you write as text by escaping it ('1999\\. was a year'). The exception is a heading rename whose find carried a heading marker: replace(find="## Old", with="## New") gives '## New', and a different level applies only when find named the heading's actual level (find "## Old" with "### New" makes it an h3), since '# ' selects a heading without naming its level. Any non-empty with that renders to no text - a line indented four spaces or a tab, which markdown reads as a code block, or whitespace alone - is INVALID_OP rather than a silent deletion; pass an empty with to delete the matched text on purpose - where the block holding it cannot be written without that paragraph, the replace is INVALID_OP and the refusal names the delete that removes it instead. ` + "with cannot open a new block: after a hard line break inside with (two trailing spaces, or a backslash, before the newline) a heading, bullet, '1.'/'1)' ordered, or '>' blockquote marker is INVALID_OP too, since that line would stay escaped text inside the matched block - use insert, plus delete for what it replaces, to add the block. A hard break in with is itself INVALID_OP when the matched text is in a heading or a table cell, which are written on one line. " + "A delete whose find is a block's entire text removes the block (a list emptied of its items goes too); delete with block removes any block by id, and move with block relocates one. delete_row and delete_column take a table block and a zero-based index, preserving the table block id and refusing to remove cells with open asks or unresolved comments. " + 'Insert and move anchors also accept "start", "end", "heading:<exact heading text>", and "block:<id>"; block ids and their tokens come from GET /api/v1/artifacts/{artifact UUID}/blocks (the route takes the artifact UUID, not its slug). ' + "Optionally require the state just read: precondition selects exactly one of a document token from dispatch_doc_read, or block {id, token} values from /blocks. A block guard must include every block the batch changes; Dispatch resolves quote targets and rejects an uncovered batch rather than applying it. Use a document token for insert or move, which depend on document order. Prefer block tokens when the covered content blocks are independent sections. Tokens include inline marks, so a fresh human comment also makes a stale edit fail. PRECONDITION_FAILED means re-read; EDIT_QUEUE_FULL means back off before retrying. " + "The result carries the document token this edit produced, so a chain of guarded edits passes each result's token as the next edit's precondition with no dispatch_doc_read between them. " + "A batch that leaves the document exactly as it was mints no version, named or not, and the result says nothing changed and names each operation that did nothing. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
30244
30244
  arguments: (z2) => ({
30245
30245
  issue: z2.string().describe(ISSUE_REFERENCE).optional(),
30246
30246
  project: z2.string().describe("Project key owning the document.").optional(),
@@ -30249,7 +30249,7 @@ var dispatchToolSpecs = [
30249
30249
  ops: z2.array(z2.object({
30250
30250
  op: z2.enum(DOC_EDIT_OPS).describe("Edit operation."),
30251
30251
  find: z2.string().describe("Text of the target as rendered, for replace or delete; inline markdown (**bold**, `code`) is tolerated and must be balanced; a leading '# ' matches a heading. A delete of a block's entire text removes the block.").optional(),
30252
- with: z2.string().describe("Replacement text for replace, parsed as inline markdown within the matched block; a marker of a different kind from the block's own is literal text, one of the same kind is refused unless it is a heading rename (where a level named by find is what lets with change it), a backslash escape keeps prose that merely looks like a marker, a block marker after a hard line break is refused because replace cannot open a new block, and any non-empty value that renders to no text is refused - only an empty value deletes the match.").optional(),
30252
+ with: z2.string().describe("Replacement text for replace: inside a code block, the code's literal text as sent (line breaks at its end do not survive a read, and a line of three or more colons in code inside a typed block, indented less than four columns from where the typed block's lines start, is refused, since the browser editor ends the typed block there: indent it four or more spaces, or move the code block out); text that would read as block syntax at a line start, such as '---' over a paragraph, is stored escaped and reads back as those characters, so a rule is added with insert beside the paragraph; elsewhere parsed as inline markdown within the matched block; a marker of a different kind from the block's own is literal text, one of the same kind is refused unless it is a heading rename (where a level named by find is what lets with change it), a backslash escape keeps prose that merely looks like a marker, a block marker after a hard line break is refused because replace cannot open a new block, and any non-empty value that renders to no text is refused - only an empty value deletes the match.").optional(),
30253
30253
  occurrence: z2.number({ int: true, min: 0 }).describe("Optional zero-based match occurrence.").optional(),
30254
30254
  markdown: z2.string().describe("Markdown to insert.").optional(),
30255
30255
  after: z2.string().describe(`Insert or move after this anchor: a quote of the neighbouring block's text, or one of "start", "end", "heading:<exact heading text>", "block:<id>".`).optional(),
package/dist/legion.js CHANGED
@@ -16142,7 +16142,7 @@ import { logger } from "@oh-my-pi/pi-utils";
16142
16142
  // package.json
16143
16143
  var package_default = {
16144
16144
  name: "@sjawhar/pi-legion-envoy",
16145
- version: "5.5.0",
16145
+ version: "5.5.2",
16146
16146
  type: "module",
16147
16147
  omp: {
16148
16148
  extensions: [
@@ -30313,7 +30313,7 @@ var dispatchToolSpecs = [
30313
30313
  ops: [{ op: "delete_column", block: "table-123", index: 1 }],
30314
30314
  precondition: { blocks: [{ id: "table-123", token: "sha256:current-table-token" }] }
30315
30315
  },
30316
- description: "Apply deterministic document edits: replace or delete quoted text, insert markdown at an anchor, retype an identified paragraph or typed block into a schema-declared typed block, delete or move a whole block by its id, or delete a table row or column in place. " + "Do not use it for review feedback or for reading; use dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. " + `For replace, delete, and quote anchors, find text as rendered: inline Markdown (**bold**, \`code\`) is tolerated and must be balanced; a leading '# ' matches a heading at any level. replace is inline: with is the new text of the matched span, so a marker of a different kind from the block's own stays literal text ('4. Design' written into a heading). A with that opens with a marker of the same kind as the matched block's own would write it twice and is INVALID_OP - including prose that merely looks like one ('1999. was a year' into an ordered item), which you write as text by escaping it ('1999\\. was a year'). The exception is a heading rename whose find carried a heading marker: replace(find="## Old", with="## New") gives '## New', and a different level applies only when find named the heading's actual level (find "## Old" with "### New" makes it an h3), since '# ' selects a heading without naming its level. Any non-empty with that renders to no text - a line indented four spaces or a tab, which markdown reads as a code block, or whitespace alone - is INVALID_OP rather than a silent deletion; pass an empty with to delete the matched text on purpose. ` + "with cannot open a new block: after a hard line break inside with (two trailing spaces, or a backslash, before the newline) a heading, bullet, '1.'/'1)' ordered, or '>' blockquote marker is INVALID_OP too, since that line would stay escaped text inside the matched block - use insert, plus delete for what it replaces, to add the block. " + "A delete whose find is a block's entire text removes the block (a list emptied of its items goes too); delete with block removes any block by id, and move with block relocates one. delete_row and delete_column take a table block and a zero-based index, preserving the table block id and refusing to remove cells with open asks or unresolved comments. " + 'Insert and move anchors also accept "start", "end", "heading:<exact heading text>", and "block:<id>"; block ids and their tokens come from GET /api/v1/artifacts/{artifact UUID}/blocks (the route takes the artifact UUID, not its slug). ' + "Optionally require the state just read: precondition selects exactly one of a document token from dispatch_doc_read, or block {id, token} values from /blocks. A block guard must include every block the batch changes; Dispatch resolves quote targets and rejects an uncovered batch rather than applying it. Use a document token for insert or move, which depend on document order. Prefer block tokens when the covered content blocks are independent sections. Tokens include inline marks, so a fresh human comment also makes a stale edit fail. PRECONDITION_FAILED means re-read; EDIT_QUEUE_FULL means back off before retrying. " + "The result carries the document token this edit produced, so a chain of guarded edits passes each result's token as the next edit's precondition with no dispatch_doc_read between them. " + "A batch that leaves the document exactly as it was mints no version, named or not, and the result says nothing changed and names each operation that did nothing. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
30316
+ description: "Apply deterministic document edits: replace or delete quoted text, insert markdown at an anchor, retype an identified paragraph or typed block into a schema-declared typed block, delete or move a whole block by its id, or delete a table row or column in place. " + "Do not use it for review feedback or for reading; use dispatch_comment, dispatch_suggest, or dispatch_doc_read instead. " + `For replace, delete, and quote anchors, find text as rendered: inline Markdown (**bold**, \`code\`) is tolerated and must be balanced; a leading '# ' matches a heading at any level. replace is inline: with is the new text of the matched span, so a marker of a different kind from the block's own stays literal text ('4. Design' written into a heading). A with that opens with a marker of the same kind as the matched block's own would write it twice and is INVALID_OP - including prose that merely looks like one ('1999. was a year' into an ordered item), which you write as text by escaping it ('1999\\. was a year'). The exception is a heading rename whose find carried a heading marker: replace(find="## Old", with="## New") gives '## New', and a different level applies only when find named the heading's actual level (find "## Old" with "### New" makes it an h3), since '# ' selects a heading without naming its level. Any non-empty with that renders to no text - a line indented four spaces or a tab, which markdown reads as a code block, or whitespace alone - is INVALID_OP rather than a silent deletion; pass an empty with to delete the matched text on purpose - where the block holding it cannot be written without that paragraph, the replace is INVALID_OP and the refusal names the delete that removes it instead. ` + "with cannot open a new block: after a hard line break inside with (two trailing spaces, or a backslash, before the newline) a heading, bullet, '1.'/'1)' ordered, or '>' blockquote marker is INVALID_OP too, since that line would stay escaped text inside the matched block - use insert, plus delete for what it replaces, to add the block. A hard break in with is itself INVALID_OP when the matched text is in a heading or a table cell, which are written on one line. " + "A delete whose find is a block's entire text removes the block (a list emptied of its items goes too); delete with block removes any block by id, and move with block relocates one. delete_row and delete_column take a table block and a zero-based index, preserving the table block id and refusing to remove cells with open asks or unresolved comments. " + 'Insert and move anchors also accept "start", "end", "heading:<exact heading text>", and "block:<id>"; block ids and their tokens come from GET /api/v1/artifacts/{artifact UUID}/blocks (the route takes the artifact UUID, not its slug). ' + "Optionally require the state just read: precondition selects exactly one of a document token from dispatch_doc_read, or block {id, token} values from /blocks. A block guard must include every block the batch changes; Dispatch resolves quote targets and rejects an uncovered batch rather than applying it. Use a document token for insert or move, which depend on document order. Prefer block tokens when the covered content blocks are independent sections. Tokens include inline marks, so a fresh human comment also makes a stale edit fail. PRECONDITION_FAILED means re-read; EDIT_QUEUE_FULL means back off before retrying. " + "The result carries the document token this edit produced, so a chain of guarded edits passes each result's token as the next edit's precondition with no dispatch_doc_read between them. " + "A batch that leaves the document exactly as it was mints no version, named or not, and the result says nothing changed and names each operation that did nothing. " + `The spec (or any document) holds requirements, design, and decisions - never progress, status, or timestamps. ${OWNER_REFERENCE} ${SPEC_WRITING_GUIDANCE}`,
30317
30317
  arguments: (z2) => ({
30318
30318
  issue: z2.string().describe(ISSUE_REFERENCE).optional(),
30319
30319
  project: z2.string().describe("Project key owning the document.").optional(),
@@ -30322,7 +30322,7 @@ var dispatchToolSpecs = [
30322
30322
  ops: z2.array(z2.object({
30323
30323
  op: z2.enum(DOC_EDIT_OPS).describe("Edit operation."),
30324
30324
  find: z2.string().describe("Text of the target as rendered, for replace or delete; inline markdown (**bold**, `code`) is tolerated and must be balanced; a leading '# ' matches a heading. A delete of a block's entire text removes the block.").optional(),
30325
- with: z2.string().describe("Replacement text for replace, parsed as inline markdown within the matched block; a marker of a different kind from the block's own is literal text, one of the same kind is refused unless it is a heading rename (where a level named by find is what lets with change it), a backslash escape keeps prose that merely looks like a marker, a block marker after a hard line break is refused because replace cannot open a new block, and any non-empty value that renders to no text is refused - only an empty value deletes the match.").optional(),
30325
+ with: z2.string().describe("Replacement text for replace: inside a code block, the code's literal text as sent (line breaks at its end do not survive a read, and a line of three or more colons in code inside a typed block, indented less than four columns from where the typed block's lines start, is refused, since the browser editor ends the typed block there: indent it four or more spaces, or move the code block out); text that would read as block syntax at a line start, such as '---' over a paragraph, is stored escaped and reads back as those characters, so a rule is added with insert beside the paragraph; elsewhere parsed as inline markdown within the matched block; a marker of a different kind from the block's own is literal text, one of the same kind is refused unless it is a heading rename (where a level named by find is what lets with change it), a backslash escape keeps prose that merely looks like a marker, a block marker after a hard line break is refused because replace cannot open a new block, and any non-empty value that renders to no text is refused - only an empty value deletes the match.").optional(),
30326
30326
  occurrence: z2.number({ int: true, min: 0 }).describe("Optional zero-based match occurrence.").optional(),
30327
30327
  markdown: z2.string().describe("Markdown to insert.").optional(),
30328
30328
  after: z2.string().describe(`Insert or move after this anchor: a quote of the neighbouring block's text, or one of "start", "end", "heading:<exact heading text>", "block:<id>".`).optional(),
@@ -580,7 +580,9 @@ a new typed block, omit `#block-id`; Dispatch mints it. When editing an existing
580
580
  its id and every rendered attribute. Never copy an existing block's id into new markdown: an id
581
581
  names one block, so an insert, upload or suggestion whose markdown names an id the document holds
582
582
  outside the text it replaces is refused naming the id: `INVALID_OP` for an insert,
583
- `INVALID_MARKDOWN` for any other write.
583
+ `INVALID_MARKDOWN` for any other write. To rewrite such a block whole, `delete` it and then
584
+ `insert` the new one carrying its id, anchored on the block before or after it, in that order and
585
+ in one batch: an insert carrying an id the document still holds is refused.
584
586
 
585
587
  Use only the type names, content rule, attributes, and enum values returned by the schema. Values are
586
588
  quoted: `:::callout{kind="warning" title="Risk"}`. Do not write Pandoc-style `::: {.callout}`, leaf
@@ -66,12 +66,23 @@ heading's actual level — `find="## Old"`, `with="### New"` retitles and makes
66
66
  so `find="# Old"` renames the text and keeps whatever level it selected. `with` that forms more than one
67
67
  paragraph is rejected (`INVALID_OP` on `with`) — see the recipe for a multi-paragraph rewrite below; so is any non-empty `with` that
68
68
  renders to no text, which a line indented four spaces or a tab does (markdown reads that as a code block), as does whitespace
69
- alone. An empty `with` is the one that deletes the matched text on purpose. Use zero-based `occurrence` for a
69
+ alone. An empty `with` deletes the matched text on purpose; where the block holding it cannot be written without that
70
+ paragraph, the replace is `INVALID_OP`, and the refusal names the `delete` that removes it instead. Inside a code
71
+ block none of this applies: `with` is the code's literal text, written as sent, whitespace, markdown syntax and
72
+ references included, except that line breaks at the end of the code's text, and a line holding only whitespace in a
73
+ list item's code, do not survive the next read; and a line of three or more colons in code inside a typed block,
74
+ indented less than four columns from where the typed block's lines start, is `INVALID_OP`, since the browser editor
75
+ ends the typed block there - indent it four or more spaces (a tab reaches only the next tab stop, which inside a list
76
+ item or a blockquote can be two columns away), or move the code block out of the typed block. Text a `replace` writes
77
+ that would read as block syntax at a line start is stored escaped and reads back as the characters you sent: `---` over
78
+ a paragraph is stored `\---`, not a rule, so to add a rule, `insert` it beside the paragraph (`insert` with markdown
79
+ `***`). Use zero-based
80
+ `occurrence` for a
70
81
  repeated target; re-read a missing or ambiguous target before retrying. Pass `summary` to name the version when recording a decision.
71
82
 
72
83
  **Rewriting several paragraphs is one `replace` per paragraph, then a read-back.** `replace` is inline:
73
- each `with` is the new text of one paragraph, and a `with` that forms two paragraphs is refused whatever the
74
- text says. Give each paragraph you rewrite its own `replace`, which keeps that paragraph's block id and every
84
+ each `with` is the new text of one paragraph, and outside a code block a `with` that forms two paragraphs is
85
+ refused whatever the text says. Give each paragraph you rewrite its own `replace`, which keeps that paragraph's block id and every
75
86
  anchor outside the text you rewrite. A comment or ask anchored to the text you rewrite loses its quote but keeps
76
87
  its pin to the block, so the dashboard still shows it beside that paragraph; a delete (below) loses both. An
77
88
  anchor that straddles the boundary keeps its mark over the words you left alone, with its quote shortened to
@@ -88,18 +99,26 @@ as literal text, except a backtick fence, which becomes an inline code span whos
88
99
  info string and line breaks included (```` ```go\nx := 1``` ```` becomes the code span `go` + a line break +
89
100
  `x := 1`), and a tilde fence, which stays literal.
90
101
 
91
- HTML is not written as text at all. A `with` whose HTML markdown reads as a block (`<div>x</div>`,
92
- `<!-- note -->`) is stored as inline HTML, and `dispatch_doc_read` then returns it raw, in markdown Dispatch
93
- will not take back: the schema carries no block HTML, so an `insert` or an upload of that markdown is refused.
94
- Keep HTML inside a line.
102
+ Outside a code block, HTML is not written as text at all, and a `replace` whose HTML would open a block where it lands is refused
103
+ (`INVALID_OP` on `with`), because the schema carries no block HTML. `<div>x</div>` and `<!-- note -->` open one
104
+ at the start of a paragraph or a list item and on the line after a hard break; a tag such as `<br>` opens one
105
+ only when it stands alone as a paragraph or a list item. The same HTML inside a line, in a table cell or in a
106
+ heading is inline HTML and is kept as written.
107
+
108
+ A hard line break in `with` (two trailing spaces or a backslash before a newline) is refused in a heading or a
109
+ table cell (`INVALID_OP` on `with`): both are written on one line, so the break would end the block there. A line
110
+ break inside a code span or inline HTML there is refused the same way. Write the text without the break, or
111
+ `insert` a new block after this one.
95
112
 
96
113
  When the new text adds a block that is not a paragraph beside paragraphs, `insert` it beside the
97
114
  paragraph you replaced, which keeps that paragraph's id; only when no paragraph of the new text is left to take
98
- the old block's place is it an `insert` of the new block plus a `delete` of the old, and the delete is what
99
- costs the id (below). Then read the document back with
115
+ the old block's place is it a `delete` of the old block and then an `insert` of the new one, anchored on the
116
+ block before or after it. The delete is what costs the id (below); a typed block keeps its id when the insert
117
+ carries it, which works only in that order, because an insert carrying an id the document still holds is refused.
118
+ Then read the document back with
100
119
  `dispatch_doc_read` and read the passage and its neighbours, not a grep for the words you added: an empty
101
- `with` deletes the matched text on purpose, so a `replace` whose `with` you meant to fill empties that
102
- paragraph — the block and its id stay, holding nothing — and only a read shows what the document now says.
120
+ `with` deletes the matched text on purpose where the block allows it, so a `replace` whose `with` you meant to fill
121
+ empties that paragraph — the block and its id stay, holding nothing — and only a read shows what the document now says.
103
122
 
104
123
  A batch that leaves the document's semantic identity unchanged — including its inline anchor marks, so an edit that only orphans a
105
124
  comment or ask anchor still mints its version — mints no version, named or not: the response carries
@@ -117,8 +136,8 @@ attributes — a moved `ask` keeps its ask and answer. **A block loses its id on
117
136
  which is why they are refused while an open ask or unresolved comment sits on them; and a `move` that takes the last block out of a
118
137
  blockquote or list item removes that emptied container, the list too when no item remains, and each enclosing container that
119
138
  held nothing else (`> - Only.` loses the blockquote as well as the item and the list). The moved block itself keeps its id,
120
- as do `replace` (an empty `with` and a heading-level change included), `insert` and `retype`; `retype` carries the paragraph's id
121
- onto the typed block it becomes. Block ids are the `#id` a typed block renders
139
+ as do `replace` (an accepted empty `with` and a heading-level change included), `insert` and `retype`; `retype` carries the
140
+ paragraph's id onto the typed block it becomes. Block ids are the `#id` a typed block renders
122
141
  (`:::ask{#5467e5ce-…}`) and, for every block including untyped ones, the `id` rows from
123
142
  `GET /api/v1/artifacts/<artifact UUID>/blocks` (or `/api/v1/issues/{key}/artifacts/{slug}/blocks`), each with its `type` and byte range
124
143
  in canonical markdown; the UUID route does not accept a slug. A later operation in the same atomic batch that names a block removed by
@@ -72,8 +72,8 @@ carries, so nothing changes in how you handle wakes. Under the TypeScript daemon
72
72
  claims the role and calls `/controller/ready` exactly as under tmux; under the Go daemon
73
73
  (`LEGION_DAEMON_API=go` in your environment) it registers on `/legion/v1/claims/register` with the
74
74
  secret, claims the role, then subscribes to `notifications.legion.<project>.controller`, where the
75
- Go daemon publishes the two Go rows of the wake routing table. The daemon records you as
76
- `controllerLocator: {runtime, external: true, sessionId, registeredAt}`, `runtime` being the
75
+ Go daemon publishes the rows marked from the Go daemon in the wake routing table. The daemon records
76
+ you as `controllerLocator: {runtime, external: true, sessionId, registeredAt}`, `runtime` being the
77
77
  daemon's own (`kubernetes`, or `tmux` under the Go daemon). The TypeScript daemon reads your
78
78
  liveness from the Envoy role registry (the holder of
79
79
  `legion-<project>-controller` and its `last_seen`), not from a pane: keep the session running.
@@ -87,15 +87,29 @@ mints a new secret, so your grants stop working and the role moves to the new se
87
87
 
88
88
  The Go daemon's controller topic is a wake for a session that is running when it is published.
89
89
  Envoy hands an Oh My Pi session no retained copy of a notice published before it subscribed, so a
90
- hold or a tree architect's failed claim from while no controller ran never arrives as a wake. At
91
- every start, before anything else, read `legion state --json` and handle each issue whose
92
- `issues.<KEY>.phase` is `held` (its `issues.<KEY>.holdReason` is `escalated` when its architect
93
- sent it to you, and absent while the architect is still deciding or while its tree lingers or is
94
- closed, where the hold waits for the tree's re-admission and needs nothing from you), and each
95
- tree root whose `issues.<KEY>.architect.state` is `failed` and whose `issues.<KEY>.phase` is not
96
- `done`, exactly as the matching wake below. A parked tree (root phase `done`: it lingers or is
97
- closed) needs nothing from you: a failed architect ignores the park and reads `failed` until the
98
- tree closes. The issue record is the truth; the topic is the wake.
90
+ hold, a tree architect's failed claim, or a new triage root from while no controller ran never
91
+ arrives as a wake. At every start, before anything else:
92
+
93
+ 1. Read `legion state --json` and handle each issue whose `issues.<KEY>.phase` is `held` (its
94
+ `issues.<KEY>.holdReason` is `escalated` when its architect sent it to you, and absent while the
95
+ architect is still deciding or while its tree lingers or is closed, where the hold waits for the
96
+ tree's re-admission and needs nothing from you), and each tree root whose
97
+ `issues.<KEY>.architect.state` is `failed` and whose `issues.<KEY>.phase` is not `done`, exactly
98
+ as the matching wake below. A parked tree (root phase `done`: it lingers or is closed) needs
99
+ nothing from you: a failed architect ignores the park and reads `failed` until the tree closes.
100
+ 2. List the project's triage issues with `dispatch_issues({project, status: "triage", limit: 250})`.
101
+ When its first line ends `(showing N of M)`, it is one page: say in your summary how many rows
102
+ it left unread. The rows show no parent, so open each row with `dispatch_read`: one whose
103
+ `Links:` name a `child_of` issue is a child, which its parent's architect owns, so leave it,
104
+ whether or not `legion state --json` records it (a `child_of` under `Referenced by:` is a child
105
+ of this issue, not its parent). Of the rest, triage each that `legion state --json` does not
106
+ record under `issues` as a new issue. A root recorded there and now in `triage` is work the
107
+ daemon holds that a human pulled back: never re-admit it yourself; name it in your summary to
108
+ the human ("<KEY> was pulled back to triage; what do you want?"). This listing is also the only
109
+ way you learn of an unrecorded root moved back into triage, or of a child detached to a root
110
+ while it is in triage, since the daemon wakes you only on a root's creation.
111
+
112
+ The issue record and Dispatch are the truth; the topic is the wake.
99
113
 
100
114
  ## Deployment instructions
101
115
 
@@ -123,7 +137,7 @@ quoted here.
123
137
 
124
138
  | Wake | Content | Controller action |
125
139
  |---|---|---|
126
- | New issue created in the Dispatch project (`issue.created`, status `triage`; resync heals misses) | issue key + triage context (incl. pre-existing children) | Triage: `legion status <KEY> todo` to admit, or set `backlog`/`icebox` to park |
140
+ | New issue created in the Dispatch project (`issue.created`, status `triage`; under the TypeScript daemon resync heals misses, under the Go daemon the boot step above does). From the Go daemon: `triage on <KEY>` (payload `{kind: "triage"}`) on the controller topic, for a root only | issue key + triage context (incl. pre-existing children) | Triage: `legion status <KEY> todo` to admit, or set `backlog`/`icebox` to park |
127
141
  | Backlog eligibility | slot freed / priority change | Reconsider parked items and move the eligible root to `todo` |
128
142
  | Architect escalation (controller-actionable only: re-file a child as a root issue, capacity, cross-tree conflicts) | request + context | Judge and act; issue-scoped human Q&A goes through `dispatch_ask` from the owning architect, not here |
129
143
  | Resync report | artifact-driven anomaly list (zero-owner trees, untriaged-open, launch-failed, admission-drift) | Verify against fresh state, then heal |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sjawhar/pi-legion-envoy",
3
- "version": "5.5.0",
3
+ "version": "5.5.2",
4
4
  "type": "module",
5
5
  "omp": {
6
6
  "extensions": [