Several in-memory runtime stores put mutable collections inside ConcurrentHashMap, which makes the map lookup concurrent but leaves the stored collection itself unsafe to mutate or iterate from multiple coroutines. InMemoryCredentialStore stores MutableList<Credential> values at lib/src/main/kotlin/dev/arcp/credentials/CredentialStore.kt:26, mutates them in record at lib/src/main/kotlin/dev/arcp/credentials/CredentialStore.kt:32, iterates and removes in remove at lib/src/main/kotlin/dev/arcp/credentials/CredentialStore.kt:40, and flattens them in pendingRevocations at lib/src/main/kotlin/dev/arcp/credentials/CredentialStore.kt:47. AgentRegistry stores MutableMap<String, Boolean> values at lib/src/main/kotlin/dev/arcp/runtime/AgentRegistry.kt:10, mutates them after computeIfAbsent at lib/src/main/kotlin/dev/arcp/runtime/AgentRegistry.kt:20, and iterates them in resolve and descriptors at lib/src/main/kotlin/dev/arcp/runtime/AgentRegistry.kt:35 and lib/src/main/kotlin/dev/arcp/runtime/AgentRegistry.kt:40.
Fix prompt: Replace the mutable values with immutable copy-on-write values updated through ConcurrentHashMap.compute, or guard each registry with a Mutex and keep all reads and writes inside the critical section. For credentials, update a job's list by returning a new list from compute and remove empty entries atomically. For agents, update each agent's version map atomically and expose descriptor snapshots that cannot observe mid-mutation state. Add coroutine tests that concurrently record/remove credentials and register/resolve agents.
Several in-memory runtime stores put mutable collections inside
ConcurrentHashMap, which makes the map lookup concurrent but leaves the stored collection itself unsafe to mutate or iterate from multiple coroutines.InMemoryCredentialStorestoresMutableList<Credential>values atlib/src/main/kotlin/dev/arcp/credentials/CredentialStore.kt:26, mutates them inrecordatlib/src/main/kotlin/dev/arcp/credentials/CredentialStore.kt:32, iterates and removes inremoveatlib/src/main/kotlin/dev/arcp/credentials/CredentialStore.kt:40, and flattens them inpendingRevocationsatlib/src/main/kotlin/dev/arcp/credentials/CredentialStore.kt:47.AgentRegistrystoresMutableMap<String, Boolean>values atlib/src/main/kotlin/dev/arcp/runtime/AgentRegistry.kt:10, mutates them aftercomputeIfAbsentatlib/src/main/kotlin/dev/arcp/runtime/AgentRegistry.kt:20, and iterates them inresolveanddescriptorsatlib/src/main/kotlin/dev/arcp/runtime/AgentRegistry.kt:35andlib/src/main/kotlin/dev/arcp/runtime/AgentRegistry.kt:40.Fix prompt: Replace the mutable values with immutable copy-on-write values updated through
ConcurrentHashMap.compute, or guard each registry with aMutexand keep all reads and writes inside the critical section. For credentials, update a job's list by returning a new list fromcomputeand remove empty entries atomically. For agents, update each agent's version map atomically and expose descriptor snapshots that cannot observe mid-mutation state. Add coroutine tests that concurrently record/remove credentials and register/resolve agents.