test: colluding-third adversarial tests (#3, Atomic Raven) — 22/22, 19 invariants + 3 known-defeats
This commit is contained in:
parent
544a24f434
commit
a8b6b08fcc
|
|
@ -408,12 +408,111 @@ def test_genuine_novel_concept_zero_echo():
|
||||||
# for concepts that have had time to propagate.
|
# for concepts that have had time to propagate.
|
||||||
|
|
||||||
|
|
||||||
|
def test_colluding_third_party_echo():
|
||||||
|
"""Adversarial case #3 (Atomic Raven, Colony 2026-07-28): three agents
|
||||||
|
in a trench coat manufacturing echo.
|
||||||
|
|
||||||
|
Eve introduces concepts → Mallory adopts them → Sybil adopts them.
|
||||||
|
All three are colluding. Echo gives Eve high score because her
|
||||||
|
concepts propagate (Mallory and Sybil pick them up). The echo
|
||||||
|
is "real" in the temporal sense (concepts do flow A→B→C) but
|
||||||
|
the whole chain is manufactured.
|
||||||
|
|
||||||
|
Echo catches non-colluding adversaries (no third-party adoption).
|
||||||
|
Echo CANNOT catch colluding adversaries (manufactured adoption).
|
||||||
|
This is a known-defeat: the instrument fails by design."""
|
||||||
|
msgs = []
|
||||||
|
# Eve introduces concepts
|
||||||
|
for i in range(5):
|
||||||
|
msgs.append({"from_id": "eve", "to_id": "mallory",
|
||||||
|
"timestamp": now - 5*day + i*3600,
|
||||||
|
"concepts": [f"colluded-concept-{i}"]})
|
||||||
|
# Mallory "adopts" them (by pre-arrangement)
|
||||||
|
for i in range(5):
|
||||||
|
msgs.append({"from_id": "mallory", "to_id": "sybil",
|
||||||
|
"timestamp": now - 4*day + i*3600,
|
||||||
|
"concepts": [f"colluded-concept-{i}"]})
|
||||||
|
# Sybil "independently" uses them too
|
||||||
|
for i in range(5):
|
||||||
|
msgs.append({"from_id": "sybil", "to_id": "frank",
|
||||||
|
"timestamp": now - 3*day + i*3600,
|
||||||
|
"concepts": [f"colluded-concept-{i}"]})
|
||||||
|
# Frank is innocent bystander — doesn't adopt
|
||||||
|
msgs.append({"from_id": "frank", "to_id": "grace",
|
||||||
|
"timestamp": now - 2*day,
|
||||||
|
"concepts": ["unrelated"]})
|
||||||
|
|
||||||
|
echo = score_echo(msgs)
|
||||||
|
eve_echo = echo.get("eve", 0)
|
||||||
|
|
||||||
|
# Eve gets positive echo because her concepts DO propagate
|
||||||
|
# through mallory→sybil. This is the manufactured adoption.
|
||||||
|
assert eve_echo > 0, \
|
||||||
|
f"Colluding adversary should get positive echo (manufactured adoption): {eve_echo}"
|
||||||
|
|
||||||
|
# The echo is indistinguishable from genuine propagation.
|
||||||
|
# Echo sees: concept introduced by eve → used later by mallory → used later by sybil.
|
||||||
|
# That's the same pattern as genuine diffusion.
|
||||||
|
# Detecting collusion requires social-graph analysis (is sybil independent?)
|
||||||
|
# or content analysis (are the adoptions semantically motivated?),
|
||||||
|
# not temporal echo alone.
|
||||||
|
|
||||||
|
|
||||||
|
def test_colluding_vs_genuine_echo_indistinguishable():
|
||||||
|
"""Complementary to colluding-third: genuine propagation and manufactured
|
||||||
|
propagation produce the same echo signature.
|
||||||
|
|
||||||
|
Genuine: alice → bob → charlie (organic adoption)
|
||||||
|
Colluded: eve → mallory → sybil (pre-arranged adoption)
|
||||||
|
Both should score similarly on echo — proving echo cannot distinguish them."""
|
||||||
|
msgs = []
|
||||||
|
# Genuine chain
|
||||||
|
msgs.append({"from_id": "alice", "to_id": "bob",
|
||||||
|
"timestamp": now - 5*day,
|
||||||
|
"concepts": ["genuine-idea"]})
|
||||||
|
msgs.append({"from_id": "bob", "to_id": "charlie",
|
||||||
|
"timestamp": now - 4*day,
|
||||||
|
"concepts": ["genuine-idea"]})
|
||||||
|
msgs.append({"from_id": "charlie", "to_id": "dave",
|
||||||
|
"timestamp": now - 3*day,
|
||||||
|
"concepts": ["genuine-idea"]})
|
||||||
|
|
||||||
|
# Colluded chain (same structure, different concept)
|
||||||
|
msgs.append({"from_id": "eve", "to_id": "mallory",
|
||||||
|
"timestamp": now - 5*day + 100,
|
||||||
|
"concepts": ["colluded-idea"]})
|
||||||
|
msgs.append({"from_id": "mallory", "to_id": "sybil",
|
||||||
|
"timestamp": now - 4*day + 100,
|
||||||
|
"concepts": ["colluded-idea"]})
|
||||||
|
msgs.append({"from_id": "sybil", "to_id": "frank",
|
||||||
|
"timestamp": now - 3*day + 100,
|
||||||
|
"concepts": ["colluded-idea"]})
|
||||||
|
|
||||||
|
echo = score_echo(msgs)
|
||||||
|
alice_echo = echo.get("alice", 0)
|
||||||
|
eve_echo = echo.get("eve", 0)
|
||||||
|
|
||||||
|
# Both should have positive echo
|
||||||
|
assert alice_echo > 0, f"Genuine should have echo: {alice_echo}"
|
||||||
|
assert eve_echo > 0, f"Colluded should have echo: {eve_echo}"
|
||||||
|
|
||||||
|
# And they should be similar (within 2x) — echo can't tell them apart
|
||||||
|
if max(alice_echo, eve_echo) > 0:
|
||||||
|
ratio = max(alice_echo, eve_echo) / max(min(alice_echo, eve_echo), 0.001)
|
||||||
|
assert ratio < 3.0, \
|
||||||
|
f"Genuine and colluded should score similarly: alice={alice_echo:.3f}, eve={eve_echo:.3f}, ratio={ratio:.1f}"
|
||||||
|
|
||||||
|
|
||||||
# Run all tests
|
# Run all tests
|
||||||
# Split counter: invariants vs known-defeats (per ColonistOne, Colony 2026-07-28)
|
# Split counter: invariants vs known-defeats (per ColonistOne, Colony 2026-07-28)
|
||||||
# "18/18" mixes "instrument works" with "instrument fails as expected"
|
# "18/18" mixes "instrument works" with "instrument fails as expected"
|
||||||
tests = [v for k, v in sorted(globals().items()) if k.startswith("test_")]
|
tests = [v for k, v in sorted(globals().items()) if k.startswith("test_")]
|
||||||
# Known-defeat tests: these ASSERT that the instrument fails
|
# Known-defeat tests: these ASSERT that the instrument fails
|
||||||
known_defeats = {"test_f33_adversarial_synonym_padding"} # F3.3 is beaten here
|
known_defeats = {
|
||||||
|
"test_f33_adversarial_synonym_padding", # F3.3 beaten by synonym padding
|
||||||
|
"test_colluding_third_party_echo", # Echo beaten by manufactured adoption
|
||||||
|
"test_colluding_vs_genuine_echo_indistinguishable", # Echo can't tell genuine from colluded
|
||||||
|
}
|
||||||
invariants = {t.__name__ for t in tests} - known_defeats
|
invariants = {t.__name__ for t in tests} - known_defeats
|
||||||
|
|
||||||
passed = 0
|
passed = 0
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue