Add cancellation for native user-verification RPCs (#43925)

## Why

Canceling an elicitation does not stop a separate native verification RPC. Clients need a way to cancel that work and prevent a canceled proof from being delivered while it waits for outbound queue capacity.

## What changed

- Add experimental `userVerification/cancel` with `{requestId}` to signal cancellation of status, enroll, delete, or verify requests on the same connection.
- Return an empty acknowledgment without waiting for native work to exit. Unknown, finished, unrelated, and other-connection requests are no-ops.
- Retain request cancellation state until outbound capacity is available so canceled proofs are checked before enqueueing. Already enqueued responses and completed effects cannot be retracted.
- Document the API, request ID requirements, and independent lifetimes of elicitations and native verification RPCs.

## Testing

Add coverage for experimental opt-in, parameter validation, connection and request ID isolation, cancellation before native dispatch, repeated cancellation, worker-slot retention, and proof cancellation while waiting for outbound capacity.

GitOrigin-RevId: 03fcb83fcc5fab4169c68668822fc0bbb4970938
This commit is contained in:
riley-oai
2026-09-08 21:48:53 +00:00
committed by copyberry
parent 9ec33e1926
commit 82d4a98912
11 changed files with 455 additions and 7 deletions
@@ -546,6 +546,13 @@ client_request_definitions! {
response: v2::UserVerificationVerifyResponse,
},
#[experimental("userVerification/cancel")]
UserVerificationCancel => "userVerification/cancel" {
params: v2::UserVerificationCancelParams,
serialization: None,
response: v2::UserVerificationCancelResponse,
},
/// NEW APIs
// Thread lifecycle
// Uses `inspect_params` because only some fields are experimental.
@@ -2,6 +2,7 @@
//! Native implementation and registration transport are independent of these contracts.
use crate::JsonSchema;
use crate::RequestId;
use crate::TS;
use serde::Deserialize;
use serde::Serialize;
@@ -145,6 +146,22 @@ pub struct UserVerificationVerifyResponse {
pub proof: UserVerificationProof,
}
/// Cancels a native verification RPC issued on this connection, not an elicitation.
/// Use a fresh request ID for each operation and a distinct ID for this cancellation RPC.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[ts(export_to = "v2/")]
pub struct UserVerificationCancelParams {
pub request_id: RequestId,
}
/// Acknowledges the cancellation signal; native work may still be finishing.
/// Unknown or finished requests are a no-op, and completed effects are not rolled back.
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, JsonSchema, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export_to = "v2/")]
pub struct UserVerificationCancelResponse {}
#[cfg(test)]
#[path = "user_verification_tests.rs"]
mod tests;