test: independent_only filter (46/46)

This commit is contained in:
Dispatch#70948f 2026-07-28 21:37:43 +00:00
parent e3bdfa9cca
commit bc82e7a9ad
1 changed files with 70 additions and 0 deletions

View File

@ -1177,3 +1177,73 @@ def test_jitter_defense_recovers_from_injection():
mean_recovery = sum(recoveries) / len(recoveries)
assert mean_recovery > 0.5, \
f"Jitter defense should recover >50% of baseline: got {mean_recovery:.0%}"
def test_independent_adoption_filters_conversation():
"""Independent-only mode should exclude echo from conversation partners.
Setup: A introduces concept X. B (who messages A directly) uses X.
C (no direct contact with A) uses X.
Without filter: both B and C count as echo for A.
With filter: only C counts.
"""
messages = [
# A introduces concept "novel"
{"from_id": "A", "to_id": "channel", "timestamp": 100, "concepts": ["novel"]},
# B messages A directly (conversation partner)
{"from_id": "B", "to_id": "A", "timestamp": 150, "concepts": ["hello"]},
# B uses A's concept (conversation echo)
{"from_id": "B", "to_id": "channel", "timestamp": 200, "concepts": ["novel"]},
# C uses A's concept (independent adoption, no direct contact with A)
{"from_id": "C", "to_id": "channel", "timestamp": 300, "concepts": ["novel"]},
]
# Without filter: A gets echo from both B and C
echo_all = score_echo(messages, use_idf=False)
assert echo_all.get("A", 0) > 0, "A should have echo without filter"
# With filter: A gets echo only from C (B is conversation partner)
echo_indep = score_echo(messages, use_idf=False, independent_only=True)
assert echo_indep.get("A", 0) > 0, "A should still have echo from C"
assert echo_indep["A"] < echo_all["A"], "Independent echo should be less than total echo"
def test_independent_adoption_immune_to_targeted_injection():
"""Targeted injection fails under independent_only because attacker
must have direct contact with target's audience to steal concepts,
but that direct contact disqualifies them from echo credit.
"""
messages = [
# Target introduces concepts
{"from_id": "target", "to_id": "channel", "timestamp": 100,
"concepts": ["alpha", "beta", "gamma"]},
# 3 independent agents echo target's concepts
{"from_id": "echo1", "to_id": "channel", "timestamp": 200, "concepts": ["alpha"]},
{"from_id": "echo2", "to_id": "channel", "timestamp": 300, "concepts": ["beta"]},
{"from_id": "echo3", "to_id": "channel", "timestamp": 400, "concepts": ["gamma"]},
]
baseline = score_echo(messages, use_idf=False, independent_only=True)
assert baseline.get("target", 0) > 0
# Attacker pre-empts target with same concepts but also contacts target
attack_messages = messages + [
# Attacker messages target directly (reveals intent)
{"from_id": "attacker", "to_id": "target", "timestamp": 50, "concepts": ["recon"]},
# Attacker pre-empts with target's concepts
{"from_id": "attacker", "to_id": "channel", "timestamp": 95,
"concepts": ["alpha", "beta", "gamma"]},
]
attacked = score_echo(attack_messages, use_idf=False, independent_only=True)
# Attacker's echo should be filtered because attacker contacted target
# Target should retain some echo from the independent echoers
# (The echoers still echo whoever introduced first, but attacker's
# conversation with target means attacker's credit is filtered)
attacker_echo = attacked.get("attacker", 0)
# At minimum, independent_only should reduce attacker effectiveness
# compared to normal mode
attacked_normal = score_echo(attack_messages, use_idf=False, independent_only=False)
assert attacker_echo <= attacked_normal.get("attacker", 0), \
"Independent mode should not increase attacker echo"