fix(telemetry): a search that never ran is not a decline (#3765)
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / Python lint (push) Successful in 4s
CI & Build / TypeScript typecheck (push) Successful in 46s
CI & Build / integration (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m21s
CI & Build / Build & push image (push) Successful in 36s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / Python lint (push) Successful in 4s
CI & Build / TypeScript typecheck (push) Successful in 46s
CI & Build / integration (push) Successful in 52s
CI & Build / Python tests (push) Successful in 1m21s
CI & Build / Build & push image (push) Successful in 36s
`best_available_score` was added by #3670 so a bar could be judged from what it rejected, and it arrived null on four unrelated causes: the corpus offered nothing, the query was empty, the embedder was down, or the DATABASE QUERY FAILED. Only the first is a measurement. The fourth is the #2663 shape — a swallowed failure rendering as a clean zero — inside the field added to fix an instance of the #2663 shape. Found while trying to explain why reuse_slot returned nothing on 45 of 45 calls, and auto_inject on 153 of 161. That investigation is still open; what it established first is that the readout could not answer it. THE FIX IS NOT A NEW COLUMN. A call that never searched writes no row, so every remaining null means one thing: searched, and nothing came close. That is the convention the pre-tool arm already follows for a blank command — "a row here would report a call that never happened and drag the clear-rate down with phantom declines" — extended from the case a caller can see in advance to the ones only the search knows about. Both searches stamp `report["searched"]` FALSE before anything can return and True only where a real result set exists, so every early return leaves it false. It has to be the first thing done to the dict: a return added above that line would leave the key absent. ABSENT IS A THIRD STATE AND IT DEFAULTS TO TRUE. A caller that passes no report cannot know, and the safe reading there is the old behaviour. Only a real search can report False, so absent means "nobody asked" and never "it failed" — which is also why 66 existing mocked searches across twelve test files keep working unchanged rather than being rewritten to simulate a flag they do not care about. A FAILURE IS NOT MADE INVISIBLE. semantic_search_notes already logs a WARNING on a query failure, which is where a broken search belongs: a counter cannot say "I am broken" without a reader already trusting it. Three tests, and the middle one is what makes them discriminate — a blanket `return` passes the first and fails the second, because a call that searched and came back empty is the only evidence a threshold is too high (#3497). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011cPyzNnegXHr5iRMzzy5KJ
This commit is contained in:
@@ -492,6 +492,14 @@ async def semantic_search_notes(
|
||||
the only figure that survives a call returning nothing, and therefore the
|
||||
only one a bar can be judged from (#3670).
|
||||
|
||||
It also sets `report["searched"]`: False before anything can return, True
|
||||
only where a real result set exists. So an empty query, an unavailable
|
||||
embedder and a failed database query all leave it FALSE, and a caller can
|
||||
tell a search that found nothing from one that never ran. A caller logging
|
||||
telemetry must check it — recording a failed search as a zero-result call
|
||||
reports a decline the ranker never made (#3765). ABSENT means no search
|
||||
touched the dict at all, which is a stand-in in a test, not a real call.
|
||||
|
||||
`note_type` narrows to a record kind, or several (e.g. "snippet", or
|
||||
("snippet", "note")), for callers that want prior art rather than everything
|
||||
embedded.
|
||||
@@ -526,6 +534,13 @@ async def semantic_search_notes(
|
||||
|
||||
Returns an empty list if the embedder is unavailable or on any error.
|
||||
"""
|
||||
# Stamped FALSE before anything can return, flipped True only where a real
|
||||
# result set exists (#3765). Every early return below leaves it false, so a
|
||||
# caller can tell a search that found nothing from one that never ran. It
|
||||
# has to be the first thing done to `report`: a return added above this
|
||||
# line would leave the key ABSENT, which reads as "no caller asked".
|
||||
if report is not None:
|
||||
report["searched"] = False
|
||||
if not query or not query.strip():
|
||||
return []
|
||||
try:
|
||||
@@ -636,6 +651,13 @@ async def semantic_search_notes(
|
||||
# The best score anything reached, bar or no bar. Recorded BEFORE the
|
||||
# filter because a call that returns nothing is exactly when it matters.
|
||||
if report is not None:
|
||||
# `searched` is what stops a null score meaning four things (#3765).
|
||||
# Every early return above — empty query, embedder down, and the broad
|
||||
# `except` around the query itself — leaves this key ABSENT, so a
|
||||
# caller can tell "I looked and there was nothing" from "I never
|
||||
# looked" and from "the query failed". Set here, at the one point past
|
||||
# which a real result set exists.
|
||||
report["searched"] = True
|
||||
report["best_available_score"] = scored[0][0] if scored else None
|
||||
scored = [pair for pair in scored if pair[0] >= threshold]
|
||||
if not demote_superseded:
|
||||
@@ -801,6 +823,14 @@ async def semantic_search_rules(
|
||||
the only figure that survives a call returning nothing, and therefore the
|
||||
only one a bar can be judged from (#3670).
|
||||
|
||||
It also sets `report["searched"]`: False before anything can return, True
|
||||
only where a real result set exists. So an empty query, an unavailable
|
||||
embedder and a failed database query all leave it FALSE, and a caller can
|
||||
tell a search that found nothing from one that never ran. A caller logging
|
||||
telemetry must check it — recording a failed search as a zero-result call
|
||||
reports a decline the ranker never made (#3765). ABSENT means no search
|
||||
touched the dict at all, which is a stand-in in a test, not a real call.
|
||||
|
||||
Scoped by OWNERSHIP — a rule is the caller's if they own its rulebook or
|
||||
its project. Deliberately not filtered to what currently BINDS a given
|
||||
project: this answers "is there a rule about this", which a person asking
|
||||
@@ -828,6 +858,9 @@ async def semantic_search_rules(
|
||||
from scribe.models.project import Project
|
||||
from scribe.models.rulebook import Rule, Rulebook, RulebookTopic
|
||||
|
||||
# See the sibling search: stamped before anything can return (#3765).
|
||||
if report is not None:
|
||||
report["searched"] = False
|
||||
if not query or not query.strip():
|
||||
return []
|
||||
try:
|
||||
@@ -874,6 +907,8 @@ async def semantic_search_rules(
|
||||
best[rule.id] = (score, rule)
|
||||
ranked = sorted(best.values(), key=lambda pair: pair[0], reverse=True)
|
||||
if report is not None:
|
||||
# See the sibling search: absent means the search never ran (#3765).
|
||||
report["searched"] = True
|
||||
report["best_available_score"] = ranked[0][0] if ranked else None
|
||||
return [pair for pair in ranked if pair[0] >= threshold][:limit]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user