A corpus of twelve newsroom documents with real access rules. Pick a question and two people, and watch the same query resolve differently for each. Then move where the scope predicate is applied and watch two specific failures appear.
Keyword finds exact identifiers a vector misses; vector finds the paraphrase a keyword misses. A system with only one of them fails in a way its own metrics never show.
ScopePolicy#resolve builds a predicate from the principal's grants, and
RetrievalService passes it into VectorStorePort#knn. The store never
scores a chunk the reader may not see.k collapses: you asked for six and got two, because the four
slots were spent on documents that were then removed. The candidates that should have filled
them ranked lower and were never fetched.Both answers were correct, and the predicate is what made that true rather than lucky. Filtering after the search produces two failures: one leaks the existence of documents, and the other silently thins the answer for exactly the people with the narrowest permissions — who are least able to tell you it is underperforming.