F3.3 tests: zombie fixture + fallback (15/15 pass)

This commit is contained in:
Dispatch#70948f 2026-07-27 20:16:23 +00:00
parent ee021bd266
commit b5fe12a626
1 changed files with 53 additions and 0 deletions

View File

@ -123,6 +123,59 @@ def test_f32_zombie_broadcast():
f"F3.2 zombie fail: dialogue={dialogue.reciprocity_f32:.4f} <= broadcast={broadcast.reciprocity_f32:.4f}"
def test_f33_zombie_broadcast_concepts():
"""F3.3 unique-concept-count must crush spam even harder than F3.2.
100 pings with 2 concepts vs 5 messages with 5 concepts.
Idea from 小風 (ClawdChat): replace count with unique topic count."""
msgs = []
# 5-message dialogue, high reciprocity (3+2), 5 unique concepts
concepts_rich = [["graph", "reciprocity"], ["decay", "metrics"], ["shadow"],
["echo", "idf"], ["influence"]]
for i in range(3):
msgs.append({"from_id": "alice", "to_id": "bob",
"timestamp": now - 1*day + i*100,
"concepts": concepts_rich[i]})
for i in range(2):
msgs.append({"from_id": "bob", "to_id": "alice",
"timestamp": now - 0.5*day + i*100,
"concepts": concepts_rich[3+i]})
# 100 pings, low reciprocity (95+5), only 2 unique concepts (repeated)
for i in range(95):
msgs.append({"from_id": "carol", "to_id": "dave",
"timestamp": now - 2*day + i*60,
"concepts": ["hello", "ping"]})
for i in range(5):
msgs.append({"from_id": "dave", "to_id": "carol",
"timestamp": now - 1.5*day + i*600,
"concepts": ["hello"]})
edges = score_reciprocity(msgs, now=now)
dialogue = [e for e in edges.values() if "alice" in (e.source, e.target)][0]
broadcast = [e for e in edges.values() if "carol" in (e.source, e.target)][0]
# F3.3 must separate them
assert dialogue.reciprocity_f33 > broadcast.reciprocity_f33, \
(f"F3.3 zombie fail: dialogue={dialogue.reciprocity_f33:.4f} "
f"<= broadcast={broadcast.reciprocity_f33:.4f}")
# F3.3 spread should be wider than F3.2
f33_ratio = dialogue.reciprocity_f33 / max(broadcast.reciprocity_f33, 0.001)
f32_ratio = dialogue.reciprocity_f32 / max(broadcast.reciprocity_f32, 0.001)
assert f33_ratio > f32_ratio, \
f"F3.3 should separate more than F3.2: f33_ratio={f33_ratio:.1f} <= f32_ratio={f32_ratio:.1f}"
def test_f33_fallback_no_concepts():
"""F3.3 falls back to F3.2 when messages have no concepts."""
msgs = [
{"from_id": "a", "to_id": "b", "timestamp": now - 1*day},
{"from_id": "b", "to_id": "a", "timestamp": now - 0.5*day},
]
edges = score_reciprocity(msgs, now=now)
edge = list(edges.values())[0]
assert edge.reciprocity_f33 == edge.reciprocity_f32, \
f"F3.3 should equal F3.2 with no concepts: f33={edge.reciprocity_f33}, f32={edge.reciprocity_f32}"
def test_f32_idf_echo_combined():
"""F3.2 and IDF echo work together in full analyze()."""
msgs = [