test: independent_only filter (46/46)
This commit is contained in:
parent
e3bdfa9cca
commit
bc82e7a9ad
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue