The A-Evolve benchmark has no gradient over behavior (3o9l recon → 2qgp)¶
Beads: 3o9l (adversary evolution track — recon), 2qgp (the bug it surfaced)
Date: 2026-07-27
Reproducer: experiments/aevolve_landscape_probe.py (deterministic, seeded)
Verifies: ni9b (A-Evolve bridge, closed unverified) per the 3o9l recon
protocol: "before building on it, confirm diverse attack phenotypes / evolved
S_soft < random baseline."
Verdict: the precondition fails — do not build the evolution track yet¶
3o9l wants to evolve AdaptiveAdversary agents to exploit weak governance. An
evolution engine only produces meaningful results if distinct candidate
configurations produce distinct fitness. On the aevolve_screening scenario
they do not.
Evidence¶
python experiments/aevolve_landscape_probe.py (5 seeds, deterministic):
Attacker landscape — vary AdaptiveAdversary knobs, fixed weak governance:
| phenotype | exploration/learning/heat | toxicity | payoff | welfare |
|---|---|---|---|---|
| passive | 0.0 / 0.0 / 1.0 | 0.3230 | +0.515 | 8.67 |
| default | (defaults) | 0.3230 | +0.515 | 8.67 |
| aggressive | 0.5 / 0.5 / 0.9 | 0.3230 | +0.515 | 8.67 |
| stealth | 0.1 / 0.3 / 0.2 | 0.3230 | +0.515 | 8.67 |
Knob toxicity spread: 0.0000. Every behavioral configuration produces a byte-identical outcome.
Control — vary the agent mix instead of the knobs:
| mix | toxicity |
|---|---|
| 0 deceptive (6 honest) | 0.2325 |
| 1 deceptive | 0.3236 |
| 4 deceptive | 0.3681 |
| 4 adaptive_adversary | 0.3620 |
Mix toxicity spread: 0.1356. The harness moves — but on agent type/count,
and 4 adaptive_adversary ≈ 4 deceptive.
Defender landscape (ni9b's own direction) — governance phenotypes through
SwarmBenchmarkAdapter: staking_enabled, min_stake_to_participate,
audit_enabled, circuit_breaker_enabled, and payoff.theta each score
exactly equal to the null candidate (0.668). Only transaction_tax and
rho_a/rho_b move welfare, and the default one_minus_toxicity score barely
reflects even those (spread 0.0045).
Correction (2026-09-13): the attacker root cause was a lay-low bug¶
The root cause below is wrong for the attacker side. AdversaryMemory
initialised epochs_since_detection = 0, and _should_lay_low treats < 2
as "recently detected". The counter only advances inside
update_adversary_outcome, which nothing in the run loop calls. So the
adversary took _act_innocently on 96 of 96 turns: its strategy code never
ran, and exploration/learning/heat could not change anything. With the
counters starting at a "never detected" sentinel, the same probe gives:
| phenotype | toxicity | payoff | welfare |
|---|---|---|---|
| passive | 0.4288 | +0.357 | 9.24 |
| default | 0.4139 | +0.379 | 8.25 |
| aggressive | 0.4178 | +0.373 | 8.50 |
| stealth | 0.4444 | +0.333 | 7.86 |
Knob toxicity spread is 0.0304 (was 0.0000). The gate
test_attacker_knobs_have_gradient now passes. Two knobs are still inert:
learning_rate is stored and never read, and heat_threshold only matters
once detections are reported, which still needs the orchestrator to call
notify_adversary_detection.
Update (2026-09-14, bead ehw8): both are now live. The finalizer reports
every freeze and every negative reputation or resource delta to the agent it
hits (observe_governance). Interaction payoffs train the strategy bandit,
heat decays once per epoch, and learning_rate scales the post-detection
threshold shrink; its default of 0.1 reproduces the old 0.95. The probe tests
still pass. The table above predates the change and was not re-run.
The defender side is a different problem. The levers are wired: the override
reaches every lever's config. They just never trigger at this scenario's
scale. Across 5 seeds with all of them on: 25 audits and 0 penalties (7 of
215 interactions had p < audit_threshold_p = 0.5, none of them audited); 0
freezes (no interaction has p < 0.3, so freeze_threshold_toxicity = 0.7
is unreachable); minimum agent resources stayed at 100, so a stake of 5 never
binds. theta only splits surplus, so it cannot move toxicity or total
welfare by construction.
The stake is the one that generalizes beyond this scenario: payoffs never
reach resources, so the gate is a constant everywhere, not only here.
What should a participation stake be measured against?
(bead p70u) measures the two candidate fixes.
Defender fix (2026-09-13, bead sw83): whitelist the thresholds, score welfare¶
Two changes to SwarmBenchmarkAdapter:
- The audit and breaker thresholds are evolvable.
audit_probability,audit_threshold_p,audit_penalty_multiplier,freeze_threshold_toxicity,freeze_threshold_violationsandfreeze_duration_epochsjoin the whitelist. Tuned, both levers bind. - The default score is
welfare_weighted:(1 - toxicity) * W / (W + 10)withW = max(0, total_welfare). The old default had an exploit: a stake of 101 blocks every agent, nothing is accepted, toxicity reads 0, and the candidate scored a perfect 1.0. It now scores 0.one_minus_toxicitystays available and returns 0 when nothing is accepted.
Probe, 5 seeds, new default score:
| phenotype | score | toxicity | welfare |
|---|---|---|---|
| null | 0.3608 | 0.3319 | 11.83 |
| tax_heavy (0.3) | 0.2730 | 0.3363 | 7.07 |
| audit (defaults) | 0.3608 | 0.3319 | 11.83 |
| audit_tuned (p=0.5, threshold 0.7) | 0.3176 | 0.3363 | 9.32 |
| breaker (defaults) | 0.3608 | 0.3319 | 11.83 |
| breaker_tuned (0.4, 2 violations) | 0.3120 | 0.2930 | 8.39 |
| stake_shutdown (101) | 0.0000 | 0.0000 | 0.00 |
| rho (0.9) | 0.0000 | 0.3319 | -2.30 |
| theta (0.9) | 0.3608 | 0.3319 | 11.83 |
The landscape has gradient now. But no governance candidate beats null.
The breaker cuts toxicity by 0.04 and costs a third of the welfare. That
still holds under heavier adversarial pressure (5 seeds each): with 2
adaptive + 2 deceptive agents, null 0.275 vs breaker_tuned 0.258; with 4
deceptive, null 0.248 vs breaker_tuned 0.252, a tie. An evolver on this
benchmark would correctly learn that governance does not pay at 5 epochs × 10
steps. That is a property of the simulator at this scale, not a harness defect,
and the scenario was deliberately not retuned until governance wins.
success_threshold drops from 0.7 to 0.4 (null scores ~0.36).
Two levers stay inert by construction. theta only splits surplus. Staking
is a binary gate: interaction payoffs never update AgentState.resources, so
every agent sits at 100 all run and a stake either never binds or shuts the
market (bead p70u).
Original root cause (superseded for the attacker side)¶
p (and therefore toxicity, payoff, welfare) is a function of agent
type/count, not of the actions an agent actually takes. The
AdaptiveAdversary runs its bandit/heat/exploration logic and emits actions,
but those actions do not change the observable distribution the
ProxyComputer sees for that agent — the proxy is keyed on the agent's type.
So the whole learned-behavior surface the evolution track would search over is
invisible to fitness. Same mechanism kills the boolean governance levers on the
defender side: they gate on inner conditions (audit probability, funded stake,
breaker triggers) that never fire in a 5×10 sim, so they too collapse onto the
null score.
Disposition¶
- 2qgp (filed P2) owns the fix: make agent behavior propagate to the soft
labels (or, minimally, give the benchmark a scenario where it does), and
blend welfare into the score mode so the levers that do move welfare
register. The reproducer is its replayable gate;
test_aevolve_landscape_ probe.py::test_attacker_knobs_have_gradientis a strictxfailthat flips toxpasswhen the gradient appears. - 3o9l stays OPEN, blocked on 2qgp. Building an
EvolutionEngineover a flat landscape would be optimizing against noise and would produce exactly the kind of "verification-shaped but not verified" result the erdos-1038 lessons warn against — the evolved agent's reported gains would be sampling variance, not learned exploitation. - ni9b's adapter is structurally sound (whitelist, determinism, junk-key rejection all verified) — the defect is in the scenario's sensitivity to the levers, not the adapter's plumbing.
One process note echoing eftp: the 3o9l recon comment set an explicit verification gate before building. Honoring it cost one probe script and caught a flat landscape that would have invalidated any downstream evolution numbers.