Hold a task's merge while another task in the project is merging

This commit is contained in:
Ian Gulliver
2026-10-15 12:11:35 -07:00
parent ff22f33025
commit 606cf4b5c0
16 changed files with 546 additions and 190 deletions
+2
View File
@@ -229,6 +229,8 @@ export function handleEvent(ev) {
break;
case "task.created":
case "task.merge_title":
case "task.merge_queued":
case "task.merge_unqueued":
case "task.merged":
case "task.discarded":
if (ev.project_id) {
+1 -1
View File
File diff suppressed because one or more lines are too long
+9 -1
View File
@@ -75,7 +75,13 @@ function taskHead(rerender) {
}
const target = state.projects.find((p) => p.id === task.project_id)?.default_branch ?? "the default branch";
const busy = sess?.state === "running" || sess?.state === "queued";
const merge = h("button", { disabled: busy || unmerged?.taskId !== task.id || !unmerged.has, title: `The agent merges ${target} in, then byre squashes into one commit on ${target} and pushes it to origin` }, "Merge");
const held = task.merge_queued_at !== undefined;
const merge = h("button", {
disabled: busy || held || unmerged?.taskId !== task.id || !unmerged.has,
title: held
? "Waiting for the merge in progress in this project to finish"
: `The agent merges ${target} in, then byre squashes into one commit on ${target} and pushes it to origin; held while another task in the project is merging`,
}, held ? "Merge held" : "Merge");
merge.onclick = async () => {
let coAuthors;
try {
@@ -848,6 +854,8 @@ function renderEvent(ev, ctx) {
}
case "task.merge_title":
return h("div", { class: "note" }, `${ev.actor} set the merge commit title to "${String(p["merge_title"])}" `, when);
case "task.merge_queued":
return h("div", { class: "note" }, `${ev.actor} asked to merge; held until the merge in progress in this project is done `, when);
case "setup.started": {
const base = Number(p["port_base"]);
return h("div", { class: "note" }, `running ${String(p["script"])} with ports ${base}–${base + 9} `, when);
+1 -1
View File
File diff suppressed because one or more lines are too long
+8 -3
View File
@@ -296,7 +296,7 @@ export interface paths {
};
get?: never;
put?: never;
/** @description Starts the same agent turn as merge-in. When that turn ends normally, byre squashes the task's worktree into one commit titled with the task's merge_title on the project's default branch, authored by the caller with a Co-authored-by trailer for each of the task's co-authors, pushes it to origin, fast-forwards the project's checkout to it, then removes the worktree and branch and marks the task merged. Refused while a turn is running or queued. */
/** @description Starts the same agent turn as merge-in. When that turn ends normally, byre squashes the task's worktree into one commit titled with the task's merge_title on the project's default branch, authored by the caller with a Co-authored-by trailer for each of the task's co-authors, pushes it to origin, fast-forwards the project's checkout to it, then removes the worktree and branch and marks the task merged. While another task in the project is merging, the merge is held instead (merge_queued_at is set) and starts when no merge is in progress in the project and the task's session is idle, oldest first. Refused while a turn is running or queued, or the merge is already held. */
post: operations["mergeTask"];
delete?: never;
options?: never;
@@ -813,6 +813,11 @@ export interface components {
forked_from?: string;
/** @description With forked_from: the n of the snapshot it was forked at. */
forked_at?: number;
/**
* Format: date-time
* @description When Merge was requested while another task in the project was merging; absent once the merge starts or when none is held.
*/
merge_queued_at?: string;
};
Todo: {
/** @description Whether the default branch has a TODO.md at its root. */
@@ -1681,13 +1686,13 @@ export interface operations {
};
requestBody?: never;
responses: {
/** @description The merge turn was started or queued. */
/** @description The merge was started or held. */
202: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["AgentSession"];
"application/json": components["schemas"]["Task"];
};
};
400: components["responses"]["Error"];
+2
View File
@@ -285,6 +285,8 @@ export function handleEvent(ev: ByreEvent): void {
break;
case "task.created":
case "task.merge_title":
case "task.merge_queued":
case "task.merge_unqueued":
case "task.merged":
case "task.discarded":
if (ev.project_id) {
+11 -2
View File
@@ -134,10 +134,16 @@ function taskHead(rerender: () => void): Head {
}
const target = state.projects.find((p) => p.id === task.project_id)?.default_branch ?? "the default branch";
const busy = sess?.state === "running" || sess?.state === "queued";
const held = task.merge_queued_at !== undefined;
const merge = h(
"button",
{ disabled: busy || unmerged?.taskId !== task.id || !unmerged.has, title: `The agent merges ${target} in, then byre squashes into one commit on ${target} and pushes it to origin` },
"Merge",
{
disabled: busy || held || unmerged?.taskId !== task.id || !unmerged.has,
title: held
? "Waiting for the merge in progress in this project to finish"
: `The agent merges ${target} in, then byre squashes into one commit on ${target} and pushes it to origin; held while another task in the project is merging`,
},
held ? "Merge held" : "Merge",
);
merge.onclick = async () => {
let coAuthors;
@@ -1008,6 +1014,9 @@ function renderEvent(ev: ByreEvent, ctx: Ctx): HTMLElement | null {
case "task.merge_title":
return h("div", { class: "note" }, `${ev.actor} set the merge commit title to "${String(p["merge_title"])}" `, when);
case "task.merge_queued":
return h("div", { class: "note" }, `${ev.actor} asked to merge; held until the merge in progress in this project is done `, when);
case "setup.started": {
const base = Number(p["port_base"]);
return h("div", { class: "note" }, `running ${String(p["script"])} with ports ${base}–${base + 9} `, when);