fix(chat): use one continuation for secret binding approval

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-11-05 19:32:13 -06:00
1 parent 1b0ee51877
commit a260ff4962
2 files changed
+29 -8

No files matched your search

@@ -50,6 +50,9 @@ const mockInteractionService = vi.hoisted(() => ({
recordSecretProposalExecutionResult: vi.fn(),
}));
const mockSecretProposals = vi.hoisted(() => ({ getById: vi.fn(), approve: vi.fn() }));
vi.mock("../services/secret-proposals.js", () => ({ createSecretProposalsService: () => mockSecretProposals }));
const mockHeartbeatService = vi.hoisted(() => ({
wakeup: vi.fn(async () => undefined),
cancelRun: vi.fn(async () => null),
@@ -1705,6 +1708,32 @@ describe("issue thread interaction routes", () => {
);
});
it("uses only the bound continuation for the production secret approval fallback", async () => {
const proposalId = "44444444-4444-4444-8444-444444444444";
mockSecretProposals.getById.mockResolvedValue({ id: proposalId, kind: "binding", originIssueId: ISSUE_ID,
interactionId: "interaction-secret-fallback", configPath: "env.DEPLOYMENT_API_KEY" });
mockSecretProposals.approve.mockResolvedValue({ status: "approved" });
mockInteractionService.acceptInteraction.mockResolvedValueOnce({ interaction: {
id: "interaction-secret-fallback", companyId: "company-1", issueId: ISSUE_ID,
kind: "request_confirmation", status: "accepted", continuationPolicy: "wake_assignee",
sourceRunId: RUN_1, payload: { version: 1, prompt: "Create binding?", secretProposal: {
version: 1, proposalId, configPath: "env.DEPLOYMENT_API_KEY" } },
result: { version: 1, outcome: "accepted" },
}, createdIssues: [] });
const app = await createApp();
const res = await request(app).post(`/api/issues/${ISSUE_ID}/interactions/interaction-secret-fallback/accept`).send({});
expect(res.status).toBe(200);
expect(mockSecretProposals.approve).toHaveBeenCalledWith("company-1", proposalId,
expect.objectContaining({ resolvedByUserId: "local-board" }));
expect(res.body.result.secretProposal.status).toBe("executed");
expect.soft(mockIssueService.addComment).not.toHaveBeenCalled();
expect.soft(mockHeartbeatService.wakeup).toHaveBeenCalledTimes(1);
expect(mockHeartbeatService.wakeup).toHaveBeenCalledWith(ASSIGNEE_AGENT_ID, expect.objectContaining({
contextSnapshot: expect.objectContaining({ issueId: ISSUE_ID, interactionId: "interaction-secret-fallback" }),
payload: expect.objectContaining({ secretProposal: expect.objectContaining({ proposalId, executionStatus: "executed" }) }),
}));
});
it("records a failed secret-proposal execution and posts a thread comment", async () => {
const proposalId = "55555555-5555-4555-8555-555555555555";
const approveSecretProposal = vi.fn().mockRejectedValue(new Error("binding failed"));
-8
View File
@@ -288,7 +288,6 @@ import { retainBacklogHumanAssignment } from "../services/human-directed-work.js
import { queueIssueAssignmentWakeup } from "../services/issue-assignment-wakeup.js";
import { shouldWakeAssigneeForIssueComment } from "../services/issue-comment-wakeup.js";
import { createSecretProposalsService } from "../services/secret-proposals.js";
import { notifySecretProposalResolution } from "../services/secret-proposal-notifications.js";
import {
renderOnboardingGreeting,
ONBOARDING_GREETING_AUTHORIZATION_REASON,
@@ -17517,13 +17516,6 @@ export function issueRoutes(
proposal: lockedProposal,
}),
});
await notifySecretProposalResolution({
proposal,
status: "approved",
userId: resolvedByUserId,
issues: svc,
heartbeat,
});
}
continuationInteraction =
await interactionSvc.recordSecretProposalExecutionResult(