Because lint does not read your CSV. It validates @user:<role> against tags.roles in the project yaml, which is a deliberately separate, hand-maintained list:
The separation is on purpose. The pool dataset is environment-specific and can be a file that does not exist on the machine running lint, or a database table, or fifty rows of which two are admins. tags.roles is the vocabulary you have agreed to use in features; the dataset is where the credentials live. A typo like @user:adnim is caught at lint time instead of at 3am when the lease fails.
Do check the two agree, though. A role that lints but has no row in the pool gives you a USER_POOL_EXHAUSTED at run time instead, which is a much slower way to learn the same thing.
Small note on the check itself: it is skipped entirely when tags.roles is empty. So a project that has never declared roles accepts @user:anything silently. Once you add the first role you are opting into validation for all of them, which surprised us the day we added one.