It is a slice, not a concurrency limit, and it is taken before anything else looks at the rows. The pool is the first N rows of the dataset in file order. With poolSize: 4 the pool is rows 0 to 3 and visual_user is not in it — not leased, not busy, simply not a member. That is why the failure is a lookup failure rather than a lease failure: the name lookup runs over the same sliced list.
The sharper version of the same problem is roles. If your first four rows are standard, problem, performance and locked, and the only admin is row five, then @user:admin fails with No users with role "admin" and a hint listing four roles that do not include it — while the admin sits there in the file you are staring at. The slice happens before the role filter, so a small poolSize can delete a role.
Two fixes. Raise poolSize to cover the rows you need, or reorder the CSV so every role appears inside the slice. Ordering a data file to satisfy a number in a different file is a smell — raise poolSize.
What poolSize is genuinely for is a guard: an environment where the accounts are shared with other teams and a run must not take more than its share.