@sjawhar/opencode-legion-envoy 7.1.2 → 7.3.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
CHANGED
|
@@ -13557,6 +13557,7 @@ function serviceSubjectLabel(subject) {
|
|
|
13557
13557
|
}
|
|
13558
13558
|
return subject;
|
|
13559
13559
|
}
|
|
13560
|
+
var MAX_ISSUE_BLOCKERS = 20;
|
|
13560
13561
|
var ISSUE_TITLE_MAX = 1000;
|
|
13561
13562
|
var MAX_ISSUE_PAGE_LIMIT = 250;
|
|
13562
13563
|
var DEFAULT_ISSUE_PAGE_LIMIT = 50;
|
|
@@ -14023,6 +14024,7 @@ var dispatchToolSpecs = [
|
|
|
14023
14024
|
project: z2.string().describe("Project key for the new issue."),
|
|
14024
14025
|
title: z2.string({ max: ISSUE_TITLE_MAX }).describe("Concise issue title, at most 1,000 characters."),
|
|
14025
14026
|
parent: z2.string().describe("Optional parent issue.").optional(),
|
|
14027
|
+
blocked_by: z2.array(z2.string({ min: 1 }), { max: MAX_ISSUE_BLOCKERS }).describe("Issue keys in the same project this issue waits on. Dispatch refuses a dependency cycle.").optional(),
|
|
14026
14028
|
external: z2.string().describe("Optional external issue reference.").optional(),
|
|
14027
14029
|
force: z2.boolean().describe("Create even though POSSIBLE_DUPLICATE listed similar issues; pass it only after reading them.").optional(),
|
|
14028
14030
|
spec: z2.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_POINTER}`).optional(),
|
|
@@ -14039,7 +14041,7 @@ var dispatchToolSpecs = [
|
|
|
14039
14041
|
status: "done",
|
|
14040
14042
|
reason: "Shipped in owner/repo#7; verified on the production dashboard."
|
|
14041
14043
|
},
|
|
14042
|
-
description: "Update an existing issue: move its lifecycle status, retitle it, replace its labels, set " + "its priority, link a URL (the pull request that delivers it, a run, a document), set its " + "route, set or clear its parent, or attach it to architecture components. Status is one of " + `${ISSUE_STATUSES.join(", ")}; outside Legion, move it yourself as the work advances; inside ` + "Legion the daemon moves it. external_links are " + "merged into the issue's existing links by URL, so linking the pull request you just opened " + "keeps every earlier link. components replaces the issue's own attachment. Closing an issue " + "(status done) requires reason, the note that says why: it is posted on the issue as a " + "message, then the issue closes, because a closed issue refuses messages, comments, and " + "artifacts; reason goes only with status done. A closed issue takes only rank, components, " + "and a reopening status (any status but done); everything else, priority included, waits " + "for the reopen. " + "priority is yours to set and a human overrides it; rank, the board's own order, is not " + "settable here. At least one " + `field besides issue is required. ${ISSUE_REFERENCE}`,
|
|
14044
|
+
description: "Update an existing issue: move its lifecycle status, retitle it, replace its labels, set " + "its priority, link a URL (the pull request that delivers it, a run, a document), set its " + "route, set or clear its parent, replace the issues it is blocked by, or attach it to " + "architecture components. Status is one of " + `${ISSUE_STATUSES.join(", ")}; outside Legion, move it yourself as the work advances; inside ` + "Legion the daemon moves it. external_links are " + "merged into the issue's existing links by URL, so linking the pull request you just opened " + "keeps every earlier link. components replaces the issue's own attachment. Closing an issue " + "(status done) requires reason, the note that says why: it is posted on the issue as a " + "message, then the issue closes, because a closed issue refuses messages, comments, and " + "artifacts; reason goes only with status done. A closed issue takes only rank, components, " + "and a reopening status (any status but done); everything else, priority included, waits " + "for the reopen. " + "priority is yours to set and a human overrides it; rank, the board's own order, is not " + "settable here. At least one " + `field besides issue is required. ${ISSUE_REFERENCE}`,
|
|
14043
14045
|
arguments: (z2) => ({
|
|
14044
14046
|
issue: z2.string().describe(ISSUE_REFERENCE),
|
|
14045
14047
|
status: z2.enum(ISSUE_STATUSES).describe("New lifecycle status.").optional(),
|
|
@@ -14050,15 +14052,16 @@ var dispatchToolSpecs = [
|
|
|
14050
14052
|
external_links: z2.array(z2.string({ min: 1 })).describe("URLs to link; merged into the issue's existing external links by URL.").optional(),
|
|
14051
14053
|
route: z2.string().describe("Route the issue to role:<name> or session:<id>; an empty string clears it.").optional(),
|
|
14052
14054
|
parent: z2.string().describe("Parent issue key in the same project; an empty string clears the parent.").optional(),
|
|
14055
|
+
blocked_by: z2.array(z2.string({ min: 1 }), { max: MAX_ISSUE_BLOCKERS }).describe("Replacement list of issue keys in the same project this issue waits on; [] clears it. Dispatch refuses a dependency cycle.").optional(),
|
|
14053
14056
|
components: componentsArgument(z2).optional()
|
|
14054
14057
|
}),
|
|
14055
14058
|
validation: {
|
|
14056
14059
|
check: (value) => {
|
|
14057
14060
|
const input = value;
|
|
14058
14061
|
const reasonFits = input.status === "done" ? typeof input.reason === "string" && input.reason.trim() !== "" : input.reason === undefined;
|
|
14059
|
-
return reasonFits && (typeof input.status === "string" || typeof input.title === "string" || Array.isArray(input.labels) || typeof input.priority === "number" || input.priority === null || Array.isArray(input.external_links) || typeof input.route === "string" || typeof input.parent === "string" || typeof input.components === "object" && input.components !== null);
|
|
14062
|
+
return reasonFits && (typeof input.status === "string" || typeof input.title === "string" || Array.isArray(input.labels) || typeof input.priority === "number" || input.priority === null || Array.isArray(input.external_links) || typeof input.route === "string" || typeof input.parent === "string" || Array.isArray(input.blocked_by) || typeof input.components === "object" && input.components !== null);
|
|
14060
14063
|
},
|
|
14061
|
-
message: "Issue update requires at least one field besides issue: status, title, labels, priority, external_links, route, parent, or components. " + "status done requires reason, a non-empty note saying why the issue is closing, posted on the issue before it closes because a closed issue refuses messages, comments, and artifacts; reason goes only with status done."
|
|
14064
|
+
message: "Issue update requires at least one field besides issue: status, title, labels, priority, external_links, route, parent, blocked_by, or components. " + "status done requires reason, a non-empty note saying why the issue is closing, posted on the issue before it closes because a closed issue refuses messages, comments, and artifacts; reason goes only with status done."
|
|
14062
14065
|
},
|
|
14063
14066
|
strict: true
|
|
14064
14067
|
},
|
|
@@ -17496,11 +17499,13 @@ async function executeDispatchTool(input) {
|
|
|
17496
17499
|
const assignee = optionalString(args, "assignee");
|
|
17497
17500
|
const components = optionalComponents(args, "components");
|
|
17498
17501
|
const labels = args.labels;
|
|
17502
|
+
const blockedBy = args.blocked_by;
|
|
17499
17503
|
try {
|
|
17500
17504
|
const created = await client.issue({
|
|
17501
17505
|
project,
|
|
17502
17506
|
title,
|
|
17503
17507
|
...parent === undefined ? {} : { parent },
|
|
17508
|
+
...Array.isArray(blockedBy) ? { blocked_by: blockedBy } : {},
|
|
17504
17509
|
...external2 === undefined ? {} : { external: external2 },
|
|
17505
17510
|
...force === undefined ? {} : { force },
|
|
17506
17511
|
...spec === undefined ? {} : { spec },
|
|
@@ -17558,6 +17563,7 @@ async function executeDispatchTool(input) {
|
|
|
17558
17563
|
const components = optionalComponents(args, "components");
|
|
17559
17564
|
const priority = optionalPriority(args, "priority");
|
|
17560
17565
|
const labels = Array.isArray(args.labels) ? args.labels : undefined;
|
|
17566
|
+
const blockedBy = Array.isArray(args.blocked_by) ? args.blocked_by : undefined;
|
|
17561
17567
|
const requestedLinks = Array.isArray(args.external_links) ? [...new Set(args.external_links)] : undefined;
|
|
17562
17568
|
let before;
|
|
17563
17569
|
try {
|
|
@@ -17591,6 +17597,7 @@ async function executeDispatchTool(input) {
|
|
|
17591
17597
|
...priority === undefined ? {} : { priority },
|
|
17592
17598
|
...route === undefined ? {} : { route },
|
|
17593
17599
|
...parent === undefined ? {} : { parent: parent === "" ? null : parent },
|
|
17600
|
+
...blockedBy === undefined ? {} : { blocked_by: blockedBy },
|
|
17594
17601
|
...components === undefined ? {} : { components },
|
|
17595
17602
|
...requestedLinks === undefined ? {} : { external_links: [...before.external_links, ...newLinks.map((url2) => ({ url: url2 }))] },
|
|
17596
17603
|
actor
|
|
@@ -17618,6 +17625,9 @@ async function executeDispatchTool(input) {
|
|
|
17618
17625
|
],
|
|
17619
17626
|
...route === undefined ? [] : [after.route === null ? "route cleared" : `route ${after.route}`],
|
|
17620
17627
|
...parent === undefined ? [] : [after.parent === null ? "parent cleared" : `parent -> ${after.parent}`],
|
|
17628
|
+
...blockedBy === undefined ? [] : [
|
|
17629
|
+
after.blocked_by.length === 0 ? "blocked_by cleared" : `blocked by ${after.blocked_by.join(", ")}`
|
|
17630
|
+
],
|
|
17621
17631
|
...components === undefined ? [] : [componentsChange(components, after.components)]
|
|
17622
17632
|
];
|
|
17623
17633
|
const adviceLines = renderAdvice(input.tool, after.key, after.advice, {
|
package/dist/src/server.js
CHANGED
|
@@ -13552,6 +13552,7 @@ function date4(params) {
|
|
|
13552
13552
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
13553
13553
|
config(en_default());
|
|
13554
13554
|
// ../contracts/src/dispatch-api.ts
|
|
13555
|
+
var MAX_ISSUE_BLOCKERS = 20;
|
|
13555
13556
|
var ISSUE_TITLE_MAX = 1000;
|
|
13556
13557
|
var MAX_ISSUE_PAGE_LIMIT = 250;
|
|
13557
13558
|
var DEFAULT_ISSUE_PAGE_LIMIT = 50;
|
|
@@ -13880,6 +13881,7 @@ var dispatchToolSpecs = [
|
|
|
13880
13881
|
project: z2.string().describe("Project key for the new issue."),
|
|
13881
13882
|
title: z2.string({ max: ISSUE_TITLE_MAX }).describe("Concise issue title, at most 1,000 characters."),
|
|
13882
13883
|
parent: z2.string().describe("Optional parent issue.").optional(),
|
|
13884
|
+
blocked_by: z2.array(z2.string({ min: 1 }), { max: MAX_ISSUE_BLOCKERS }).describe("Issue keys in the same project this issue waits on. Dispatch refuses a dependency cycle.").optional(),
|
|
13883
13885
|
external: z2.string().describe("Optional external issue reference.").optional(),
|
|
13884
13886
|
force: z2.boolean().describe("Create even though POSSIBLE_DUPLICATE listed similar issues; pass it only after reading them.").optional(),
|
|
13885
13887
|
spec: z2.string().describe(`Optional initial primary-document markdown. ${SPEC_WRITING_POINTER}`).optional(),
|
|
@@ -13896,7 +13898,7 @@ var dispatchToolSpecs = [
|
|
|
13896
13898
|
status: "done",
|
|
13897
13899
|
reason: "Shipped in owner/repo#7; verified on the production dashboard."
|
|
13898
13900
|
},
|
|
13899
|
-
description: "Update an existing issue: move its lifecycle status, retitle it, replace its labels, set " + "its priority, link a URL (the pull request that delivers it, a run, a document), set its " + "route, set or clear its parent, or attach it to architecture components. Status is one of " + `${ISSUE_STATUSES.join(", ")}; outside Legion, move it yourself as the work advances; inside ` + "Legion the daemon moves it. external_links are " + "merged into the issue's existing links by URL, so linking the pull request you just opened " + "keeps every earlier link. components replaces the issue's own attachment. Closing an issue " + "(status done) requires reason, the note that says why: it is posted on the issue as a " + "message, then the issue closes, because a closed issue refuses messages, comments, and " + "artifacts; reason goes only with status done. A closed issue takes only rank, components, " + "and a reopening status (any status but done); everything else, priority included, waits " + "for the reopen. " + "priority is yours to set and a human overrides it; rank, the board's own order, is not " + "settable here. At least one " + `field besides issue is required. ${ISSUE_REFERENCE}`,
|
|
13901
|
+
description: "Update an existing issue: move its lifecycle status, retitle it, replace its labels, set " + "its priority, link a URL (the pull request that delivers it, a run, a document), set its " + "route, set or clear its parent, replace the issues it is blocked by, or attach it to " + "architecture components. Status is one of " + `${ISSUE_STATUSES.join(", ")}; outside Legion, move it yourself as the work advances; inside ` + "Legion the daemon moves it. external_links are " + "merged into the issue's existing links by URL, so linking the pull request you just opened " + "keeps every earlier link. components replaces the issue's own attachment. Closing an issue " + "(status done) requires reason, the note that says why: it is posted on the issue as a " + "message, then the issue closes, because a closed issue refuses messages, comments, and " + "artifacts; reason goes only with status done. A closed issue takes only rank, components, " + "and a reopening status (any status but done); everything else, priority included, waits " + "for the reopen. " + "priority is yours to set and a human overrides it; rank, the board's own order, is not " + "settable here. At least one " + `field besides issue is required. ${ISSUE_REFERENCE}`,
|
|
13900
13902
|
arguments: (z2) => ({
|
|
13901
13903
|
issue: z2.string().describe(ISSUE_REFERENCE),
|
|
13902
13904
|
status: z2.enum(ISSUE_STATUSES).describe("New lifecycle status.").optional(),
|
|
@@ -13907,15 +13909,16 @@ var dispatchToolSpecs = [
|
|
|
13907
13909
|
external_links: z2.array(z2.string({ min: 1 })).describe("URLs to link; merged into the issue's existing external links by URL.").optional(),
|
|
13908
13910
|
route: z2.string().describe("Route the issue to role:<name> or session:<id>; an empty string clears it.").optional(),
|
|
13909
13911
|
parent: z2.string().describe("Parent issue key in the same project; an empty string clears the parent.").optional(),
|
|
13912
|
+
blocked_by: z2.array(z2.string({ min: 1 }), { max: MAX_ISSUE_BLOCKERS }).describe("Replacement list of issue keys in the same project this issue waits on; [] clears it. Dispatch refuses a dependency cycle.").optional(),
|
|
13910
13913
|
components: componentsArgument(z2).optional()
|
|
13911
13914
|
}),
|
|
13912
13915
|
validation: {
|
|
13913
13916
|
check: (value) => {
|
|
13914
13917
|
const input = value;
|
|
13915
13918
|
const reasonFits = input.status === "done" ? typeof input.reason === "string" && input.reason.trim() !== "" : input.reason === undefined;
|
|
13916
|
-
return reasonFits && (typeof input.status === "string" || typeof input.title === "string" || Array.isArray(input.labels) || typeof input.priority === "number" || input.priority === null || Array.isArray(input.external_links) || typeof input.route === "string" || typeof input.parent === "string" || typeof input.components === "object" && input.components !== null);
|
|
13919
|
+
return reasonFits && (typeof input.status === "string" || typeof input.title === "string" || Array.isArray(input.labels) || typeof input.priority === "number" || input.priority === null || Array.isArray(input.external_links) || typeof input.route === "string" || typeof input.parent === "string" || Array.isArray(input.blocked_by) || typeof input.components === "object" && input.components !== null);
|
|
13917
13920
|
},
|
|
13918
|
-
message: "Issue update requires at least one field besides issue: status, title, labels, priority, external_links, route, parent, or components. " + "status done requires reason, a non-empty note saying why the issue is closing, posted on the issue before it closes because a closed issue refuses messages, comments, and artifacts; reason goes only with status done."
|
|
13921
|
+
message: "Issue update requires at least one field besides issue: status, title, labels, priority, external_links, route, parent, blocked_by, or components. " + "status done requires reason, a non-empty note saying why the issue is closing, posted on the issue before it closes because a closed issue refuses messages, comments, and artifacts; reason goes only with status done."
|
|
13919
13922
|
},
|
|
13920
13923
|
strict: true
|
|
13921
13924
|
},
|
package/package.json
CHANGED
|
@@ -85,7 +85,7 @@ issues you own as well as the root. An issue left at `triage` while work is unde
|
|
|
85
85
|
Waiting for the deploy lane is not a status and is never announced.
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
|
-
# PATCH /api/v1/issues/{key} — status, title, labels, priority, external links (merged by URL), route, parent
|
|
88
|
+
# PATCH /api/v1/issues/{key} — status, title, labels, priority, external links (merged by URL), route, parent, blocked_by
|
|
89
89
|
dispatch issue-update --issue PROJ-175 --status testing
|
|
90
90
|
dispatch issue-update --issue PROJ-175 --status done --reason 'Shipped in owner/repo#7; verified on the production dashboard.'
|
|
91
91
|
dispatch issue-update --issue PROJ-175 --priority 1 # 0–3; see Priority is yours to set
|
|
@@ -305,11 +305,11 @@ line), the full definition of a proof, what the tester verifies, and the simplif
|
|
|
305
305
|
- **The merge gate**, in order: the tester's evidence green → the reviewer's approval of the head
|
|
306
306
|
→ retro → the merger's READY → the merge → the implementer's production check. On READY's
|
|
307
307
|
acceptance the merger submits the pull request with
|
|
308
|
-
`gh pr merge <n> -R <owner>/<repo> --auto --squash --match-head-commit <head>`,
|
|
309
|
-
|
|
308
|
+
`gh pr merge <n> -R <owner>/<repo> --auto --squash --match-head-commit <head>`, unless the
|
|
309
|
+
answer's note begins `MANUAL_MERGE_REQUIRED:`, where a person merges it; the repository's
|
|
310
|
+
required reviews and checks decide when it lands
|
|
310
311
|
(`skill://legion-worker/references/merge-gate.md`). The reviewer's submissions, retro's commit,
|
|
311
|
-
the merger's READY and its submission, the
|
|
312
|
-
production check follow the same reference.
|
|
312
|
+
the merger's READY and its submission, and the production check follow the same reference.
|
|
313
313
|
- **No surface reaches the changed path** is a report to the architect, never a reason to
|
|
314
314
|
complete the phase: `skill://legion-worker/references/pr-body.md`.
|
|
315
315
|
|
|
@@ -360,13 +360,13 @@ The daemon reads no handoff file and no branch head; it refuses, before recordin
|
|
|
360
360
|
|
|
361
361
|
- `HANDOFF_NOT_NEW` — the commit is the one you reported for your previous phase: write and
|
|
362
362
|
commit this phase's handoff before completing.
|
|
363
|
-
-
|
|
364
|
-
|
|
363
|
+
- each `READY_…` refusal of the merger's READY — named, with its remedy, in the merger's role
|
|
364
|
+
prompt (`skill://legion-worker/references/merge-gate.md` says how READY is checked).
|
|
365
365
|
- `GITHUB_READ_FAILED` — GitHub's failure, not the head's, reading READY's checks: complete again.
|
|
366
366
|
|
|
367
367
|
Such a refusal records nothing, so the corrected call at the same commit is applied. The answer
|
|
368
368
|
may carry a `note` (READY published on an already-merged pull request, or a base requiring no
|
|
369
|
-
check).
|
|
369
|
+
check, or the `MANUAL_MERGE_REQUIRED:` note of a pull request a person merges).
|
|
370
370
|
|
|
371
371
|
Retro's last commit removes `.legion/<issue>/` from the head that merges (`skill://legion-retro`);
|
|
372
372
|
a round after it, such as one a withdrawn READY sends back, writes and commits its own handoff
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# The merge gate: review, retro, READY, and the production check
|
|
2
2
|
|
|
3
3
|
Part of `skill://legion-worker`. Read it when you are the reviewer submitting a review or an
|
|
4
|
-
approval, the implementer
|
|
5
|
-
|
|
4
|
+
approval, the implementer recording the production check, or the merger building READY and
|
|
5
|
+
submitting the merge. Every path it cites is in sjawhar/legion.
|
|
6
6
|
|
|
7
7
|
The order, in full: the tester's evidence green → the reviewer's approval of the head →
|
|
8
8
|
retro → the merger's READY → the merge → the implementer's production check. The approved
|
|
@@ -97,45 +97,46 @@ review posted without a completion leaves the issue in reviewing until you finis
|
|
|
97
97
|
- READY is `handoff_complete` with `ready: true` and the packet as its `summary`; the merger runs
|
|
98
98
|
no pre-flight of its own, and its completion reports the commit the workspace stands on. READY's
|
|
99
99
|
checks run in the daemon, which reads the pull request's head on GitHub and refuses, before
|
|
100
|
-
recording anything
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
`
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
`
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
100
|
+
recording anything, on each ground the merger's role prompt names in its `Go daemon workflow`
|
|
101
|
+
part, by its code and with its remedy. That part is the one complete list of READY's refusals.
|
|
102
|
+
A refusal records nothing; the merger reports it to the architect unless it says to complete
|
|
103
|
+
again, and never pushes a commit to make CI run. On success the daemon posts the packet as a
|
|
104
|
+
`dispatch message` on the issue, publishes it to the project's merge queue role when one is set,
|
|
105
|
+
and says on the issue when that role has no live holder; the answer's `note` says when READY was
|
|
106
|
+
published on a pull request a person already merged, whose head can no longer change, or on a
|
|
107
|
+
base that requires no check, which the packet itself also says, or carries the by-hand note of a
|
|
108
|
+
pull request a person merges.
|
|
109
|
+
- **The merger submits the merge the moment READY is accepted, unless the answer says a person
|
|
110
|
+
merges it.** In the same turn, before anything else, it runs
|
|
111
|
+
`gh pr merge <n> -R <owner>/<repo> --auto --squash --match-head-commit <head>` with its pane's
|
|
112
|
+
plain `gh`: `<n>` the pull request number, `<head>` the full sha of the current head READY named.
|
|
113
|
+
A repository with a merge queue enqueues the pull request; one without arms auto-merge; one whose
|
|
114
|
+
rules are already satisfied merges at once. `--match-head-commit` has GitHub refuse the request
|
|
115
|
+
when the head has moved since READY read it. The repository's required reviews and checks decide
|
|
116
|
+
when it lands: the merger makes no merge by hand and bypasses no rule. It then tells the
|
|
117
|
+
architect with `envoy_publish` to its encoded role token what the command answered — enqueued,
|
|
118
|
+
auto-merge enabled, or merged — and the head. When the answer's `note` begins
|
|
119
|
+
`MANUAL_MERGE_REQUIRED:` (the pull request touches a path the project's `merge.by_hand` names),
|
|
120
|
+
READY stands but the merger submits nothing: it tells the architect the note and the head, and a
|
|
121
|
+
person merges under the repository's rules. The daemon tells that READY with the note at its head
|
|
122
|
+
and the packet's `Merge:` line replaced, so the issue and the merge queue role are never asked to
|
|
123
|
+
arm it. On any READY refusal it submits nothing, since an armed request would merge through the
|
|
124
|
+
gate the refusal names; a READY the design gate refused is submitted later, on the architect's
|
|
125
|
+
word, once the gate opens, and that turn runs only `gh` and a read of the READY the daemon posted
|
|
126
|
+
then: a refusal or a posted READY carrying the by-hand note is submitted by nobody. A push that
|
|
127
|
+
moves the head past a READY the gate holds voids it before then: the architect is told
|
|
128
|
+
`READY_HEAD_MOVED` and has the merger build a new READY at the new head. A
|
|
129
|
+
`gh pr merge` GitHub refuses is reported to the architect with its message; a person can still
|
|
130
|
+
merge after READY. The packet's second line records the step,
|
|
131
|
+
`Merge: submit on normal acceptance with gh pr merge <n> -R <owner>/<repo> --auto --squash --match-head-commit <head>`.
|
|
132
|
+
- **The daemon disarms a merge no READY covers.** Auto-merge stays enabled across pushes, and
|
|
133
|
+
`--match-head-commit` is checked only when it is enabled, so the daemon itself disables the pull
|
|
134
|
+
request's auto-merge and takes it out of a merge queue, as the implement App, on any push or
|
|
135
|
+
reopen but one landing on an awaiting READY's head, when READY is withdrawn (a push or reopen
|
|
136
|
+
moved the head past it, its CI turned red, or it conflicts with its base, each of which sends the
|
|
137
|
+
issue back to `implementing`), when a child is set back to `todo`, and when the issue leaves the
|
|
138
|
+
workflow. A by-hand READY covers no arm, so whoever arms it is undone. READY waits
|
|
139
|
+
(`READY_DISARM_PENDING`) until that disarm has finished. No role runs a disarm.
|
|
139
140
|
|
|
140
141
|
## After the merge
|
|
141
142
|
|