fix(explain): withhold status-filtered candidates summaries - #651
Merged
plind-junior merged 2 commits intoJul 30, 2026
Merged
Conversation
a retracted/superseded/redacted claim or archived page correctly reported gate: "status-filtered", but explain_ranking sourced summaries from `scoped` (post scope-filter, pre status-filter), so its full live text still came back regardless of which stage actually dropped it. vouchdev#640 fixed the equivalent leak for the scope_filter stage, moving summaries off the raw fused set. that fix's chosen source, `scoped`, is itself still upstream of `_filter_live_hits`, so the same withholding was never extended to status_filter. source summaries from `live` instead — the set that survived both the scope and status gates, matching what kb.search/kb.context expose. new tests mirror vouchdev#640's scope-filtered regression test for a superseded claim and an archived page, asserting an empty summary and that the secret text is absent from the dumped response. Fixes vouchdev#650
Contributor
|
diff coverage: n/a — this PR changes no python under |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
explain_ranking()now sources candidatesummaryvalues fromlive(thepost-status-filter candidate set) instead of
scoped(post-scope-filter,pre-status-filter), so a retracted/superseded/redacted claim or archived
page that's correctly reported
gate: "status-filtered"no longer carriesits full live text in the response.
Why
#640fixed this exact leak class for thescope_filterstage — aviewer-invisible candidate keeps its gate attribution but not its text,
matching what
kb.search/kb.contextalready withhold for that viewer.That fix's chosen source,
scoped, is itself computed before_filter_live_hitsruns (the stage that drops retracted/superseded/redacted claims and archived pages), so the same withholding was never
extended to the
status_filterstage. A status-filtered candidate'ssummary was still looked up from the pre-status-filter dict regardless of
which stage actually dropped it — confirmed with a repro: a
SUPERSEDEDclaim containing a secret string came back verbatim in the JSON response
even though its gate correctly said
status-filtered.Fixes #650
What might break
Nothing for users with an existing
.vouch/directory — no on-disk shape,kb.*method, or object model change. Behaviorally:summaryis now""for any candidate whose gate is
status-filtered(it was previously thelive claim/page text); every other gate value's summary is unaffected,
since
liveandscopedonly differ by which artifacts were removed forbeing retracted/superseded/redacted/archived.
VEP
Not applicable — no object model,
kb.*method, on-disk layout, bundleformat, or audit-log shape change. A one-line fix to which candidate set a
read-only introspection surface sources its summaries from.
Tests
make check-equivalent: ruff clean (src+tests); mypyclean on
explain_ranking.py; all 33tests/test_explain_ranking.pycases pass (31 pre-existing + 2 new)
test_status_filtered_claim_does_not_carry_its_summary,test_status_filtered_page_does_not_carry_its_summary(mirroringfix(explain): scope-filtered candidates leak their summary to the viewer #640's
test_scope_filtered_candidate_does_not_carry_its_summary)CHANGELOG.mdupdated under## [Unreleased]