Category: bug Severity: major
Location: Sources/ARCP/Runtime/JobManager.swift:426-458, Sources/ARCP/Runtime/ARCPRuntime.swift:262-288
Spec: ARCP v1.1 §15.4
What
The handler runs in a child Task (runJob), so requestPermission's await permissionRegistry.awaitResponse does not directly block the JobManager actor. But the response that unblocks it (permission.grant/permission.deny) arrives through the same dispatchLoop → handle → jobManager.handlePermissionGrant. The dispatch loop processes envelopes one at a time and awaits each handle call. The current code avoids deadlock only because runJob is a separate Task; if any handler does synchronous (non-Task) permission requests directly on the JobManager actor (e.g., a future refactor or the inline credential issuance path), the actor is occupied and the grant cannot be delivered. This invariant is undocumented and untested. The 300s hardcoded timeout (.seconds(300)) also ignores the caller-supplied leaseSeconds/any client deadline.
Evidence
let outcome = try await permissionRegistry.awaitResponse(id: id, deadline: timeout)
called with timeout: .seconds(300) hardcoded in ConcreteJobContext.requestPermission. Resolution depends entirely on the serial dispatchLoop delivering permission.grant.
Proposed fix
- Document (and add a regression test for) the invariant that handler execution must run off the JobManager actor's serial executor so permission responses can be delivered.
- Make the permission timeout configurable rather than a hardcoded 300s.
Acceptance criteria
Category: bug Severity: major
Location:
Sources/ARCP/Runtime/JobManager.swift:426-458,Sources/ARCP/Runtime/ARCPRuntime.swift:262-288Spec: ARCP v1.1 §15.4
What
The handler runs in a child
Task(runJob), sorequestPermission'sawait permissionRegistry.awaitResponsedoes not directly block the JobManager actor. But the response that unblocks it (permission.grant/permission.deny) arrives through the samedispatchLoop→handle→jobManager.handlePermissionGrant. The dispatch loop processes envelopes one at a time andawaits eachhandlecall. The current code avoids deadlock only becauserunJobis a separate Task; if any handler does synchronous (non-Task) permission requests directly on the JobManager actor (e.g., a future refactor or the inline credential issuance path), the actor is occupied and the grant cannot be delivered. This invariant is undocumented and untested. The 300s hardcoded timeout (.seconds(300)) also ignores the caller-suppliedleaseSeconds/any client deadline.Evidence
called with
timeout: .seconds(300)hardcoded inConcreteJobContext.requestPermission. Resolution depends entirely on the serialdispatchLoopdeliveringpermission.grant.Proposed fix
Acceptance criteria