src/Arcp.Runtime/Internal/JobSubmitFlow.fs handles jobs.TryClaimIdempotencyKey inside handleAsync, but the Error err branch only writes an error response and then falls through to construct a JobRecord, register it, issue credentials, send job.accepted, and launch the handler. A race between two submissions with the same idempotency key can therefore both emit an error response and still create a second job, violating the idempotency contract and potentially running duplicate agent work.
Fix prompt: restructure the idempotency claim block in JobSubmitFlow.handleAsync so a failed TryClaimIdempotencyKey is terminal for that request. Return immediately after EnvelopeOut.respondWithError, or refactor the branch into a Result/taskResult flow where job creation only happens after the claim succeeds or no key was supplied. Add a concurrency test that submits the same idempotency key twice in parallel and asserts exactly one job is accepted and launched while the other request replays the existing acceptance or receives a single deterministic response without launching duplicate work.
src/Arcp.Runtime/Internal/JobSubmitFlow.fshandlesjobs.TryClaimIdempotencyKeyinsidehandleAsync, but theError errbranch only writes an error response and then falls through to construct aJobRecord, register it, issue credentials, sendjob.accepted, and launch the handler. A race between two submissions with the same idempotency key can therefore both emit an error response and still create a second job, violating the idempotency contract and potentially running duplicate agent work.Fix prompt: restructure the idempotency claim block in
JobSubmitFlow.handleAsyncso a failedTryClaimIdempotencyKeyis terminal for that request. Return immediately afterEnvelopeOut.respondWithError, or refactor the branch into aResult/taskResultflow where job creation only happens after the claim succeeds or no key was supplied. Add a concurrency test that submits the same idempotency key twice in parallel and asserts exactly one job is accepted and launched while the other request replays the existing acceptance or receives a single deterministic response without launching duplicate work.