Skip to content

test(kb): the read guard follows one hop of module-local filter helpers and recognises declared caller-independent visibility predicates #16714

Description

@mrveiss

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:

  1. Per module, collect the functions whose own body calls a FILTER_HELPERS name. Those are the filtering helpers.
  2. 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.

  • A declared CALLER_INDEPENDENT_PREDICATES set, 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.
  • A structural test requires every registered predicate's definition to actually decide visibility, by referencing the SYSTEM/PUBLIC scope levels or is_visible/check_access. Registering a trivial function fails.
  • The _get_or_compute_category_counts entry leaves the allowlist, and the ceiling is lowered to match, as re-measured under RATCHET_BASELINES.md.

Sequenced after #16703 merges, because it's the same guard file.

Refs #16667, #16691, #16654

Activity

  1. changed the title [-]test(kb): the read guard follows one hop of module-local filter helpers[/-] [+]test(kb): the read guard follows one hop of module-local filter helpers and recognises declared caller-independent visibility predicates[/+] on Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions