fix: prevent duplicate messages on unconfirmed delivery and deduplicate review prompt
sendWithConfirmation now treats unconfirmed delivery as a soft success since the message was already sent, preventing the dispatch hash from staying stale and causing duplicate sends on the next poll cycle. review-check command now sources its prompt from the lifecycle reaction config instead of hardcoding it, keeping both paths aligned. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a6c9dbd599
commit
77685a5d81
|
|
@ -12,7 +12,7 @@ interface ReviewInfo {
|
|||
reviewDecision: string | null;
|
||||
}
|
||||
|
||||
const REVIEW_FIX_PROMPT =
|
||||
const DEFAULT_REVIEW_FIX_PROMPT =
|
||||
"There are review comments on your PR. Check with `gh pr view --comments` and `gh api` for inline comments. Address each one, push fixes, and reply.";
|
||||
|
||||
async function checkPRReviews(
|
||||
|
|
@ -74,6 +74,11 @@ export function registerReviewCheck(program: Command): void {
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
// Source prompt from lifecycle reaction config so review-check and
|
||||
// the lifecycle worker stay aligned.
|
||||
const reviewFixPrompt =
|
||||
config.reactions["changes-requested"]?.message ?? DEFAULT_REVIEW_FIX_PROMPT;
|
||||
|
||||
const sm = await getSessionManager(config);
|
||||
const sessions = await sm.list(projectId);
|
||||
|
||||
|
|
@ -129,7 +134,7 @@ export function registerReviewCheck(program: Command): void {
|
|||
|
||||
if (!opts.dryRun) {
|
||||
try {
|
||||
await sm.send(result.sessionId, REVIEW_FIX_PROMPT);
|
||||
await sm.send(result.sessionId, reviewFixPrompt);
|
||||
console.log(chalk.green(` -> Fix prompt sent`));
|
||||
} catch (err) {
|
||||
console.error(chalk.red(` -> Failed to send: ${err}`));
|
||||
|
|
|
|||
|
|
@ -1279,7 +1279,7 @@ describe("send", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("throws when delivery cannot be confirmed", async () => {
|
||||
it("resolves when delivery cannot be confirmed (message already sent)", async () => {
|
||||
writeMetadata(sessionsDir, "app-1", {
|
||||
worktree: "/tmp",
|
||||
branch: "main",
|
||||
|
|
@ -1291,9 +1291,11 @@ describe("send", () => {
|
|||
vi.mocked(mockAgent.detectActivity).mockReturnValue("idle");
|
||||
|
||||
const sm = createSessionManager({ config, registry: mockRegistry });
|
||||
await expect(sm.send("app-1", "Fix the CI failures")).rejects.toThrow(
|
||||
"could not be confirmed",
|
||||
);
|
||||
// Should resolve without throwing — the message was already sent via
|
||||
// sendMessage, so unconfirmed delivery is treated as a soft success
|
||||
// to avoid duplicate dispatches on the next poll cycle.
|
||||
await expect(sm.send("app-1", "Fix the CI failures")).resolves.toBeUndefined();
|
||||
expect(mockRuntime.sendMessage).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("throws for nonexistent session", async () => {
|
||||
|
|
|
|||
|
|
@ -1154,7 +1154,12 @@ export function createSessionManager(deps: SessionManagerDeps): SessionManager {
|
|||
}
|
||||
}
|
||||
|
||||
throw new Error(`Message to session ${sessionId} could not be confirmed`);
|
||||
// Message was already sent via runtimePlugin.sendMessage above — if we
|
||||
// cannot *confirm* delivery (e.g. agent is slow to show output), treat it
|
||||
// as a soft success rather than throwing. Throwing here caused the caller
|
||||
// to report failure, which prevented the dispatch-hash from updating and
|
||||
// led to duplicate messages on the next poll cycle.
|
||||
return;
|
||||
};
|
||||
|
||||
let prepared = await prepareSession();
|
||||
|
|
|
|||
Loading…
Reference in New Issue