The 10 s concurrent-load bound also hit slow but healthy first imports. A
second agent built while a provider's import took longer than 10 s got no
memory provider for its whole session, and with checkpoint_required every
compaction then failed closed.
The bound now applies only inside plugin_loader.bounded_load_wait(), which
the per-turn gateway memory identity signature uses. Other callers (agent
builds) wait for the in-flight load. Cycle detection still breaks a
cross-thread deadlock for every caller.
Review fixes for the per-module plugin load lock:
- A second caller waits at most 10 s for another thread's load of the same
module, then refuses with a named warning and returns None. While that
import is still stuck, later callers refuse at once. A hung provider import
no longer blocks every gateway turn, and there is no inline fallback load.
- The wait polls a wait-for graph spanning the loader locks and importlib's
own module locks. When thread A holds a helper's import lock and waits on
the plugin while thread B loads the plugin and imports that helper, the
cycle is detected and the waiter takes the partial module, the same way
importlib handles a concurrent circular import. Before, both threads hung.
- The lock is still a same-thread RLock with no worker thread, so recursive
loads and ContextVars are unchanged.
- conversation_compression.py: compact the fork-checkpoint exemption so the
file stays under its line cap after the rebase.