Wait for signoff fixture runs before stage decisions

This commit is contained in:
Dotta committed 2026-10-11 00:06:59 -05:00
1 parent 0421a3b8c0
commit 9c678b2d90
1 file changed
+27
+27
View File
@@ -270,6 +270,29 @@ async function getIssueRunLockState(board: APIRequestContext, issueId: string):
};
}
async function waitForFixtureRun(
board: APIRequestContext,
agentId: string,
issueId: string,
runId: string,
) {
// Negative authorization cases deliberately invoke a non-participant. Leave
// their API rejection intact instead of waiting for a run they cannot own.
if ((await getIssueRunLockState(board, issueId)).assigneeAgentId !== agentId) return;
const waitBudgetMs = wakeupWaitBudgetMs();
const deadline = Date.now() + waitBudgetMs;
let status: string | null = null;
do {
const run = await fetchVerifiedRun(board, runId, agentId, issueId);
expect(run, "Fixture run must belong to the acting agent and issue").not.toBeNull();
status = run!.status;
if (status === "succeeded") return;
expect(["queued", "running"], `Fixture run ${runId} ended as ${status}`).toContain(status);
await new Promise((resolve) => setTimeout(resolve, WAKEUP_POLL_INTERVAL_MS));
} while (Date.now() < deadline);
throw new Error(`Fixture run ${runId} did not succeed within ${waitBudgetMs}ms (last status: ${status})`);
}
async function retryAgentPatchWithCurrentLockOnConflict(
board: APIRequestContext,
agent: AgentAuth,
@@ -325,6 +348,9 @@ async function agentPatch(
}: { maxAttempts?: number; backoffMs?: number; maxBackoffMs?: number } = {},
) {
const runId = await invokeHeartbeat(board, agent.agentId, issueId);
// The fixture process only prints "done". Let its startup and cleanup settle
// before a simulated decision reassigns the issue and cancels that process.
await waitForFixtureRun(board, agent.agentId, issueId, runId);
const patchWith = (patchRunId: string) =>
agent.request.patch(`${BASE_URL}/api/issues/${issueId}`, {
headers: { "X-Paperclip-Run-Id": patchRunId },
@@ -353,6 +379,7 @@ async function agentCheckoutAndPatch(
patchData: Record<string, unknown>,
) {
const runId = await invokeHeartbeat(board, agent.agentId, issueId);
await waitForFixtureRun(board, agent.agentId, issueId, runId);
const directPatchRes = await agent.request.patch(`${BASE_URL}/api/issues/${issueId}`, {
headers: { "X-Paperclip-Run-Id": runId },
data: patchData,