返回 last30days-skill
test_ranking_query_scaffolding.py
根目录 / tests / test_ranking_query_scaffolding.py
1 """Contract test: planner ranking-query scaffolding must not score as topic signal."""
2 import pytest
3 from lib import relevance
4 from lib import planner
5
6
7 # Ranking-query templates emitted by planner.py. Placeholders are substituted
8 # with sentinels so only the fixed scaffolding words remain.
9 _RANKING_TEMPLATES = (
10 "What recent evidence from the last 30 days is most relevant to {X}?",
11 "What recent evidence from the last 30 days is most relevant to {X}, especially about {Y}?",
12 "What recent evidence from the last 30 days is most relevant to {X} in the comparison '{Y}'?",
13 "What are the current odds, forecasts, or market signals about {X}?",
14 "What new reactions or follow-up reporting from the last 30 days matter for {X}?",
15 "What real-world workflows or pipelines are people running with {X}?",
16 "What production deployments or real-world use cases of {X} are people describing?",
17 "What hands-on experience reports or reviews of {X} exist in the last 30 days?",
18 )
19
20 # Template words deliberately NOT added to LOW_SIGNAL_QUERY_TOKENS because they
21 # are domain nouns that can legitimately be a user's topic. Globally demoting
22 # them would harm relevance for every source. They only appear in the
23 # intent-specific templates, which fire because the topic already carries that
24 # intent, so their presence is correlated with the topic rather than noise.
25 _ACCEPTED_DOMAIN_NOUNS = frozenset({
26 "cases", "deployments", "experience", "forecasts", "market", "pipelines",
27 "production", "reactions", "reporting", "reports", "signals", "workflows",
28 })
29
30 _SENTINELS = {"zzqqsentinel", "wwqqsentinel"}
31
32
33 def _fixed_tokens():
34 tokens = set()
35 for tpl in _RANKING_TEMPLATES:
36 filled = tpl.replace("{X}", "zzqqsentinel").replace("{Y}", "wwqqsentinel")
37 tokens |= relevance.tokenize(filled)
38 return tokens - _SENTINELS
39
40
41 def test_every_ranking_template_word_is_classified():
42 """No planner scaffolding word may silently count as informative topic signal."""
43 unclassified = sorted(
44 t for t in _fixed_tokens()
45 if t not in relevance.LOW_SIGNAL_QUERY_TOKENS
46 and t not in _ACCEPTED_DOMAIN_NOUNS
47 )
48 assert not unclassified, (
49 "planner ranking-query template words are neither low-signal nor an "
50 f"accepted domain noun: {unclassified}. Add them to "
51 "LOW_SIGNAL_QUERY_TOKENS, or to _ACCEPTED_DOMAIN_NOUNS with a reason."
52 )
53
54
55 def test_base_template_contributes_no_informative_tokens():
56 """The base template must not inflate coverage; only the topic should."""
57 q = planner._ranking_query("Peter Steinberger steipete", "")
58 prepared = relevance.PreparedQuery(q)
59 assert prepared.informative_q_tokens <= {"peter", "steinberger", "steipete"}, (
60 f"base ranking query leaks scaffolding into informative tokens: "
61 f"{sorted(prepared.informative_q_tokens)}"
62 )
63
64
65 def test_core_qualified_branch_contributes_no_informative_tokens():
66 """The `especially about <core>` branch must not leak `especially` either."""
67 q = planner._ranking_query("Peter Steinberger steipete", "steipete")
68 prepared = relevance.PreparedQuery(q)
69 assert "especially" not in prepared.informative_q_tokens, (
70 f"core-qualified branch leaks 'especially': {sorted(prepared.informative_q_tokens)}"
71 )
72
73
74 def test_off_topic_post_still_scores_below_floor():
75 """Discrimination must survive: unrelated content stays low."""
76 q = planner._ranking_query("Peter Steinberger steipete", "")
77 prepared = relevance.PreparedQuery(q)
78 score = relevance.token_overlap_relevance(
79 prepared, "Sourdough starter tips for cold kitchens in winter baking"
80 )
81 assert score < 0.15, f"off-topic post scored {score}, expected below the 0.15 floor"
82
83
84 def test_zero_overlap_post_remains_zero():
85 """Pins the boundary this unit cannot cross; U2's exemption owns these."""
86 q = planner._ranking_query("Peter Steinberger steipete", "")
87 prepared = relevance.PreparedQuery(q)
88 score = relevance.token_overlap_relevance(
89 prepared, "cli was a year ago. apps maybe 6 months. now it's services."
90 )
91 assert score == 0.0, (
92 f"expected 0.0 for a post sharing no token with the query, got {score}. "
93 "If this changes, KTD7's division of labour between U1 and U2 needs revisiting."
94 )
95
95 lines PYTHON