src/Internal/Runtime/ToolInvocationHandler.php:111 handles a repeated (principal, idempotency_key) by emitting a correlated Ack('replay') and returning before the tool runs. ARCPClient::invokeTool() only treats ToolResult, ToolError, and Nack as valid terminal responses, so the duplicate request either raises InvalidArgumentException for an unexpected Ack or times out depending on timing. The integration test in tests/Integration/JobLifecycleTest.php:123 documents this mismatch and intentionally avoids asserting a replayed value.
This breaks the public retry contract described in docs/guides/jobs.md:36 and docs/recipes.md:19, where callers are told to pass an IdempotencyKey so retries do not double-submit mutating work. Preventing the second execution is only half of the idempotency contract; the retry still needs to resolve consistently for the client.
Fix prompt: Change logical idempotency replay so the runtime replays or reconstructs the original terminal correlated response for the duplicate command. Store enough outcome data in the event log or query the stored outcome_message_id and send the original ToolResult or ToolError with the new request's correlation id. Update the idempotency integration test to assert that the second invokeTool() returns the same value without re-running the handler.
src/Internal/Runtime/ToolInvocationHandler.php:111handles a repeated(principal, idempotency_key)by emitting a correlatedAck('replay')and returning before the tool runs.ARCPClient::invokeTool()only treatsToolResult,ToolError, andNackas valid terminal responses, so the duplicate request either raisesInvalidArgumentExceptionfor an unexpectedAckor times out depending on timing. The integration test intests/Integration/JobLifecycleTest.php:123documents this mismatch and intentionally avoids asserting a replayed value.This breaks the public retry contract described in
docs/guides/jobs.md:36anddocs/recipes.md:19, where callers are told to pass anIdempotencyKeyso retries do not double-submit mutating work. Preventing the second execution is only half of the idempotency contract; the retry still needs to resolve consistently for the client.Fix prompt: Change logical idempotency replay so the runtime replays or reconstructs the original terminal correlated response for the duplicate command. Store enough outcome data in the event log or query the stored
outcome_message_idand send the originalToolResultorToolErrorwith the new request's correlation id. Update the idempotency integration test to assert that the secondinvokeTool()returns the same value without re-running the handler.