Follow-up to PR #16703 (#16667). Raised by a7 from #16691.
Problem
repo_tests/kb_read_visibility_guard_test.py marks a function filtered only when its own body calls one of FILTER_HELPERS. A function that filters through a same-module helper therefore reads as unfiltered, and needs an allowlist entry even though it is correct. For example, #16691's _expand_fact_relations and _process_relations_for_citations both go through _related_fact_is_accessible, which calls filter_search_results_by_permission. #16691 had to keep both as SCOPED entries.
Proposal
Resolve exactly one hop, within the same module:
- Per module, collect the functions whose own body calls a
FILTER_HELPERS name. Those are the filtering helpers.
- Mark a function filtered when it calls one of those by its bare name, or as
self.<name>.
This keeps the guard from becoming tautological:
- Cross-module helpers and chains of two or more hops are not followed, and that is declared as a blind spot.
- The laxity is the same the guard already accepts within a single function, where a filter call anywhere in the body counts, so it adds none.
Acceptance criteria
Second case: caller-independent visibility predicates (added from #16706)
#16706 had to restore the _get_or_compute_category_counts entry. Its fix filters through _visible_to_every_authenticated_user, a deliberately caller-independent predicate: one cached answer holds for every signed-in user. That name isn't one of the guard's six FILTER_HELPERS. It isn't a one-hop case, but the gap is the same one: a correct filter the guard can't see.
Sequenced after #16703 merges, because it's the same guard file.
Refs #16667, #16691, #16654
Follow-up to PR #16703 (#16667). Raised by a7 from #16691.
Problem
repo_tests/kb_read_visibility_guard_test.pymarks a function filtered only when its own body calls one ofFILTER_HELPERS. A function that filters through a same-module helper therefore reads as unfiltered, and needs an allowlist entry even though it is correct. For example, #16691's_expand_fact_relationsand_process_relations_for_citationsboth go through_related_fact_is_accessible, which callsfilter_search_results_by_permission. #16691 had to keep both asSCOPEDentries.Proposal
Resolve exactly one hop, within the same module:
FILTER_HELPERSname. Those are the filtering helpers.self.<name>.This keeps the guard from becoming tautological:
Acceptance criteria
SCOPEDentries leave the allowlist, andUNFILTERED_READ_CEILINGis lowered to match. Any other entry the change clears is removed the same way. That is re-measured and listed perdocs/developer/RATCHET_BASELINES.md.Second case: caller-independent visibility predicates (added from #16706)
#16706 had to restore the
_get_or_compute_category_countsentry. Its fix filters through_visible_to_every_authenticated_user, a deliberately caller-independent predicate: one cached answer holds for every signed-in user. That name isn't one of the guard's sixFILTER_HELPERS. It isn't a one-hop case, but the gap is the same one: a correct filter the guard can't see.CALLER_INDEPENDENT_PREDICATESset, keyed by (defining module, function name) rather than by bare name, is recognised as filtering. A call counts only when it resolves to that definition: made inside the defining module, or through a name imported from it. A same-named function anywhere else does not count, so naming a function can't satisfy the guard.is_visible/check_access. Registering a trivial function fails._get_or_compute_category_countsentry leaves the allowlist, and the ceiling is lowered to match, as re-measured underRATCHET_BASELINES.md.Sequenced after #16703 merges, because it's the same guard file.
Refs #16667, #16691, #16654