fix(router): make automerge comments fish friendly

This commit is contained in:
Vincent Koc 2026-04-29 01:11:14 -07:00
parent 99866c84da
commit 6c1fe749a3
No known key found for this signature in database
4 changed files with 49 additions and 28 deletions

View File

@ -194,9 +194,9 @@ export function renderResponse(command, dispatched) {
if (command.intent === "stop") {
return [
marker,
"Got it. Clownfish will leave this item for human review.",
"Got it. Clownfish is floating this back to human review. 🐠",
"",
"I kept the regular `clownfish` label on it and paused the automation trail until a maintainer asks again.",
"I kept the regular `clownfish` label on it and paused the automation current until a maintainer calls me back in.",
].join("\n");
}
if (command.intent === "automerge") {
@ -204,14 +204,14 @@ export function renderResponse(command, dispatched) {
return [
marker,
dispatched?.clawsweeper
? "Clownfish automerge is enabled for this PR."
: "Clownfish could not enable automerge for this PR.",
? "Clownfish is on the reef for this PR. 🐠"
: "Clownfish could not catch the automerge current for this PR.",
"",
dispatched?.clawsweeper
? `I ${clearedHumanReview ? "cleared `clownfish:human-review`, " : ""}added \`clownfish:automerge\` and asked ClawSweeper to review this head. If ClawSweeper requests changes or returns \`needs-human\`, I will repair/rebase the branch and ask for another review, up to the configured round limit.`
? `I ${clearedHumanReview ? "cleared \`clownfish:human-review\`, " : ""}tagged \`clownfish:automerge\` and sent ClawSweeper over this exact head. If the sweep finds rough coral, failing checks, or \`needs-human\`, I will take another bounded repair lap and ask for a fresh review.`
: `Reason: ${command.reason ?? "automerge requires a pull request"}.`,
"",
"A maintainer can pause this with `/clownfish stop`.",
"A maintainer can call `/clownfish stop` any time and I will drift this back to human review.",
].join("\n");
}
if (command.intent === "autoclose") {
@ -242,23 +242,23 @@ export function renderResponse(command, dispatched) {
return [
marker,
fromNeedsHuman
? "Thanks, ClawSweeper. Clownfish is continuing the automerge repair loop for this PR."
: "Thanks, ClawSweeper. Clownfish picked up the repair feedback.",
? "Thanks, ClawSweeper. Clownfish is swimming another guarded repair lap for this PR. 🐠"
: "Thanks, ClawSweeper. Clownfish picked up the reef notes and is starting a guarded repair pass. 🐠",
"",
`Source: \`${command.trusted_bot_author ?? command.author ?? "trusted automation"}\``,
`Feedback: ${command.repair_reason ?? "ClawSweeper requested another repair pass."}`,
`Action: dispatched \`${dispatched.workflow}\` for \`${dispatched.job_path}\` in \`${dispatched.mode}\` mode.`,
`Model: \`${dispatched.model}\``,
"",
"I will update this PR branch, or open a safe credited replacement, if the repair worker finds a narrow fix.",
"I will update this PR branch when I can. If GitHub branch permissions block that swim lane, I will open a safe credited replacement instead and keep it narrow.",
].join("\n");
}
if (command.intent === "clawsweeper_auto_merge") {
return [
marker,
dispatched?.merge?.status === "executed"
? "Thanks, ClawSweeper. Clownfish merged this PR after the passing review."
: "Thanks, ClawSweeper. Clownfish saw the passing review, but did not merge yet.",
? "Thanks, ClawSweeper. Clear water: Clownfish merged this PR after the passing review. 🐠"
: "Thanks, ClawSweeper. Clownfish saw the passing review, but one reef gate still blocked the merge.",
"",
`Source: \`${command.trusted_bot_author ?? command.author ?? "trusted automation"}\``,
`Feedback: ${command.repair_reason ?? "ClawSweeper reported a passing review."}`,
@ -266,36 +266,36 @@ export function renderResponse(command, dispatched) {
...(dispatched?.merge?.merged_at ? [`Merged at: ${dispatched.merge.merged_at}`] : []),
"",
dispatched?.merge?.status === "executed"
? "The automerge loop is complete."
: "I left the PR open for the remaining gate instead of bypassing it.",
? "Automerge lap complete. no mystery bubbles."
: "I left the PR open for the remaining gate instead of cutting around it.",
].join("\n");
}
if (command.intent === "clawsweeper_needs_human") {
return [
marker,
"Clownfish is pausing automerge for human review.",
"Clownfish is floating this PR back to human review. 🐠",
"",
`Source: \`${command.trusted_bot_author ?? command.author ?? "trusted automation"}\``,
`Reason: ${command.repair_reason ?? "ClawSweeper requested human review."}`,
"",
"I kept the regular `clownfish` label on it and left the final call with a maintainer.",
"I kept the regular `clownfish` label on it and left the final call with a maintainer. no sneaky merge currents.",
].join("\n");
}
if (!dispatched) {
return [
marker,
"Clownfish did not dispatch a repair worker for this one.",
"Clownfish did not send a repair worker into this current.",
"",
`Reason: ${command.reason ?? "unsupported command or target"}.`,
"",
"Supported repair commands work on existing Clownfish PRs and PRs opted into `clownfish:automerge`: `/clownfish fix ci`, `/clownfish address review`, `/clownfish rebase`.",
"A maintainer can opt a PR in with `/clownfish automerge` and I can take another pass.",
"A maintainer can opt a PR in with `/clownfish automerge`, and I can take another guarded swim.",
"A maintainer can close unsupported or declined work with `/autoclose <reason>`.",
].join("\n");
}
return [
marker,
"Clownfish picked this up.",
"Clownfish picked this up and is swimming it through the narrow lane. 🐠",
"",
`Command: \`${command.command}\``,
`Action: dispatched \`${dispatched.workflow}\` for \`${dispatched.job_path}\` in \`${dispatched.mode}\` mode.`,

View File

@ -72,6 +72,22 @@ const sourceStaysOpenLines = [
"This PR stays open for now, with the replacement linked as the current fix path.",
];
const automergeNoChangeOpeners = [
"This repair lap finished without changing the PR. Clownfish checked the reef and found no safe patch to push this time.",
"Clownfish finished this automerge repair swim without changing the branch.",
"No new branch changes from this lap. Clownfish kept the current tidy instead of splashing around.",
"This pass ended as a no-op: no narrow repair surfaced, so Clownfish left the branch untouched.",
"Clownfish took another look and did not find a safe branch change to make on this pass.",
];
const automergeNoChangeClosers = [
"No branch push, rebase, replacement PR, merge, or ClawSweeper re-review was started on this lap.",
"No push, rebase, replacement PR, merge, or ClawSweeper re-review happened this swim.",
"Clownfish left the PR as-is: no push, no rebase, no replacement PR, no merge, and no fresh ClawSweeper pass.",
"Nothing moved downstream from this pass: no branch update, replacement PR, merge, or re-review.",
"This lap stayed observational only. no branch push, no replacement, no merge, no mystery bubbles.",
];
const carriedCreditLines = [
"Contributor credit is carried into the replacement PR body and changelog plan.",
"Contributor credit is copied into the replacement PR notes and changelog path.",
@ -185,9 +201,9 @@ export function repairContributorBranchComment({ sourcePrUrl, validationCommands
export function automergeRepairOutcomeComment({ marker, result, report, target, provenance }) {
const lines = [
marker,
`${SIGNATURE} automerge status`,
`${SIGNATURE} reef automerge status`,
"",
"Repair pass finished without changing this PR.",
variant(automergeNoChangeOpeners),
"",
`Target: #${target}`,
`Executor outcome: ${compactForComment(report?.reason ?? "no executable fix action", 260)}.`,
@ -198,7 +214,7 @@ export function automergeRepairOutcomeComment({ marker, result, report, target,
if (actionLines.length > 0) {
lines.push("", "Worker actions:", ...actionLines);
}
lines.push("", "No branch push, rebase, replacement PR, merge, or ClawSweeper re-review was started by this pass.");
lines.push("", variant(automergeNoChangeClosers));
return withFishNotes(lines, provenance);
}

View File

@ -229,7 +229,7 @@ test("renderResponse reports trusted repair dispatches without losing guardrails
assert.match(body, /clownfish-command:456:2026-04-29T07:12:31Z:clawsweeper_auto_repair:def456/);
assert.match(body, /cluster-worker\.yml/);
assert.match(body, /safe credited replacement/);
assert.match(body, /narrow fix/);
assert.match(body, /keep it narrow/);
assert.doesNotMatch(body, /ProjectClownfish/i);
});
@ -250,7 +250,9 @@ test("renderResponse reports automerge resume actions", () => {
);
assert.match(body, /cleared `clownfish:human-review`/);
assert.match(body, /repair\/rebase/);
assert.match(body, /tagged `clownfish:automerge`/);
assert.match(body, /bounded repair lap/);
assert.match(body, /\/clownfish stop/);
});
test("renderResponse reports maintainer autoclose results", () => {
@ -295,9 +297,11 @@ test("renderResponse reports needs-human automerge repair dispatches", () => {
},
);
assert.match(body, /continuing the automerge repair loop/);
assert.match(body, /guarded repair lap/);
assert.match(body, /cluster-worker\.yml/);
assert.match(body, /automerge-openclaw-openclaw-74156/);
assert.match(body, /safe credited replacement/);
assert.match(body, /keep it narrow/);
assert.doesNotMatch(body, /did not dispatch/);
});
@ -313,7 +317,7 @@ test("renderResponse reports explicit human-review pause actions", () => {
null,
);
assert.match(body, /pausing automerge/);
assert.match(body, /human review/);
assert.match(body, /regular `clownfish` label/);
assert.doesNotMatch(body, /clownfish:human-review/);
assert.doesNotMatch(body, /did not dispatch/);
@ -338,7 +342,7 @@ test("renderResponse reports automerge completion", () => {
);
assert.match(body, /merged this PR/);
assert.match(body, /automerge loop is complete/);
assert.match(body, /Automerge lap complete/);
assert.doesNotMatch(body, /ProjectClownfish/i);
});

View File

@ -23,9 +23,10 @@ test("automergeRepairOutcomeComment explains no-op repair runs", () => {
});
assert.match(body, /^<!-- marker -->/);
assert.match(body, /Repair pass finished without changing this PR/);
assert.match(body, /Clownfish 🐠 reef automerge status/);
assert.match(body, /(without changing|without changing the branch|no-op|no new branch changes|no safe branch change)/i);
assert.match(body, /Executor outcome: no planned fix actions\./);
assert.match(body, /`route_security` on `#74156`: planned - central handling required/);
assert.match(body, /No branch push, rebase, replacement PR, merge, or ClawSweeper re-review/);
assert.match(body, /(No branch push|No push|left the PR as-is|Nothing moved downstream|observational only)/i);
assert.match(body, /model gpt-test, reasoning medium; reviewed against 0123456789ab/);
});