Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@ All notable changes to vouch are documented here. Format follows

## [Unreleased]

### Fixed
- the entity-salience reflex skipped the retired-claim exclusion every
other read surface applies, so a superseded, archived or redacted claim
still counted toward an entity's `claim_count` and could be handed back
as its `top_claim_id` — pointing an agent at redacted text the reviewer
had already pulled. `_meta.vouch_salience` now counts live claims only,
consistent with context, graph, digest, health and experts (#78).

## [1.4.0] — 2026-07-17

### Added
Expand Down
13 changes: 13 additions & 0 deletions src/vouch/salience.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,23 @@
from typing import Any

from . import index_db
from .models import ClaimStatus
from .storage import KBStore

DEFAULT_WINDOW = 8
DEFAULT_TOP_K = 3
_EXPIRY_SECONDS = 30 * 60

# a superseded / archived / redacted claim is not live evidence, so it must
# neither inflate an entity's claim_count nor surface as its top_claim_id
# (consistent with issue #78, and with the same exclusion in context, graph,
# digest, health and experts).
_RETIRED_CLAIM_STATUSES = frozenset({
ClaimStatus.SUPERSEDED,
ClaimStatus.ARCHIVED,
ClaimStatus.REDACTED,
})


@dataclass
class _SessionBuffer:
Expand Down Expand Up @@ -141,6 +152,8 @@ def compute_salience(
# Claims referencing each matched entity, by claim id (for stable picking).
claims_by_entity: dict[str, list[str]] = {}
for claim in store.list_claims():
if claim.status in _RETIRED_CLAIM_STATUSES:
continue
for eid in claim.entities:
if eid in scores:
claims_by_entity.setdefault(eid, []).append(claim.id)
Expand Down
27 changes: 26 additions & 1 deletion tests/test_salience.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import pytest

from vouch import health, salience
from vouch.models import Claim, Entity, EntityType
from vouch.models import Claim, ClaimStatus, Entity, EntityType
from vouch.storage import KBStore


Expand Down Expand Up @@ -45,6 +45,31 @@ def test_record_then_compute_highlights_entity(store: KBStore) -> None:
assert rec["top_claim_id"] == "c1"


@pytest.mark.parametrize(
"status",
[ClaimStatus.SUPERSEDED, ClaimStatus.ARCHIVED, ClaimStatus.REDACTED],
)
def test_retired_claims_are_not_salient(store: KBStore, status: ClaimStatus) -> None:
# a retired claim is not live evidence: it must neither inflate claim_count
# nor surface as top_claim_id. "c0" sorts before the live "c1", so a
# retired claim that is still counted wins the top slot.
src = store.put_source(b"auth notes")
store.put_claim(
Claim(
id="c0",
text="auth used basic auth",
evidence=[src.id],
entities=["jwt"],
status=status,
)
)
for _ in range(3):
salience.record_query("sess-1", "jwt")
rec = salience.compute_salience(store, "sess-1")[0]
assert rec["claim_count"] == 1
assert rec["top_claim_id"] == "c1"


def test_attach_adds_meta_when_enabled(store: KBStore) -> None:
for _ in range(3):
salience.record_query("sess-1", "jwt")
Expand Down
Loading