fix: harden the legacy HTTP rollback path
N-06: export one idempotency-key authority from mutation-intent.ts and use it in the V2 client. A caller-supplied key is validated before credentials, timers and fetch, and an invalid value is rejected as VALIDATION_REJECTED / IDEMPOTENCY_KEY_INVALID rather than trimmed, regenerated or dropped, so a keyed command can no longer replay while sending no key. N-07: bound the legacy credential wait by the existing attempt controller, which already carries the total deadline and the caller signal, so a non-cooperative owner cannot hold the request open and no extra timer is introduced. The owner receives the operation context, and the failure follows ownership: deadline to REQUEST_TIMEOUT, caller to REQUEST_ABORTED, and only a genuine rejection to AUTH_INTEGRATION_FAILURE. None of these paths fetch. N-08: readBoundedJson delegates to the common bounded reader, so cancel and releaseLock throws stay isolated inside the closed result, and the V2 content-type mismatch now cancels the response body. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4fe924ee0f
commit
c9e820aed5
@@ -307,6 +307,9 @@ describe("HTTP operation execution contract", () => {
|
||||
operationId: "LIST_ENTITIES",
|
||||
routeId: "TEST_ROUTE",
|
||||
});
|
||||
// The credential wait is bounded by the same attempt controller, so wait
|
||||
// until the request is actually in flight before firing the deadline.
|
||||
await vi.waitFor(() => expect(fetcher).toHaveBeenCalledTimes(1));
|
||||
await vi.waitFor(() => expect(scheduler.callbacks).toHaveLength(1));
|
||||
scheduler.callbacks[0]();
|
||||
await expect(timeoutResult).resolves.toMatchObject({
|
||||
|
||||
Reference in New Issue
Block a user