diff --git a/README.md b/README.md
index 8ceebbd..3b48069 100644
--- a/README.md
+++ b/README.md
@@ -58,8 +58,11 @@ now nearly reaches the mathematical ceiling of the non-injective sender
codebooks, so another horizon-only extension is not justified. Batch 26 added
one sender/receiver route cycle and reached 85.38% validation, but did not add
a single net unique code: shared errors became link-specific errors instead.
-The replay-routing family is therefore closed; future work must target sender
-capacity directly. See
+Batch 27 directly penalized globally mined sender collisions after step 30,000.
+It reduced collision multiplicity and reached 85.38% validation, but unique
+code counts again remained unchanged and worst-link validation regressed to
+82.13%. Both replay routing and the existing collision-loss schedule are now
+closed; future work must change sender representation or code occupancy. See
[`docs/development-log-ecp7.md`](docs/development-log-ecp7.md).
## Build on this work
@@ -126,6 +129,7 @@ Do not translate those artifacts in place. New experiment configurations and all
- [`config/ecp7-b24-development.yaml`](config/ecp7-b24-development.yaml) — Batch 23 routing with sender messages detached for receiver-only replay after step 20,000.
- [`config/ecp7-b25-development.yaml`](config/ecp7-b25-development.yaml) — Batch 24 receiver-only catch-up with an isolated 45,000-step maximum horizon.
- [`config/ecp7-b26-development.yaml`](config/ecp7-b26-development.yaml) — Batch 25 with one preregistered sender/receiver replay-route cycle.
+- [`config/ecp7-b27-development.yaml`](config/ecp7-b27-development.yaml) — Batch 25 plus one late bounded direct sender-collision pulse.
- [`config/ecp7-positive-control-development.yaml`](config/ecp7-positive-control-development.yaml) — ECP-6 positive control on the ECP-7 split.
- [`config/ecp0.yaml`](config/ecp0.yaml) — machine-readable configuration from step 1.
- [`config/ecp1.yaml`](config/ecp1.yaml) — frozen configuration of the population trial.
diff --git a/config/ecp7-b27-development.yaml b/config/ecp7-b27-development.yaml
new file mode 100644
index 0000000..b0b42d5
--- /dev/null
+++ b/config/ecp7-b27-development.yaml
@@ -0,0 +1,24 @@
+extends: ecp7-b25-development.yaml
+
+experiment:
+ title: ECP-7 late bounded sender-collision pulse
+ arm: late_bounded_sender_collision_pulse
+ objective: >-
+ Test whether a direct bounded separation pulse on globally mined sender
+ collisions can increase codebook capacity after receivers have saturated,
+ without sacrificing the established weak-structure reconstruction result.
+
+training:
+ global_collision_replay:
+ enabled: true
+ weight: 0.1
+ start_step: 30000
+ warmup_steps: 5000
+ pair_batch_size: 32
+ refresh_interval: 200
+ relaxed_temperature: 1.0
+ weight_decay:
+ enabled: true
+ start_step: 35000
+ end_step: 40000
+ final_weight: 0.0
diff --git a/docs/AI_AGENT_START.md b/docs/AI_AGENT_START.md
index a3fd820..f0a2d26 100644
--- a/docs/AI_AGENT_START.md
+++ b/docs/AI_AGENT_START.md
@@ -41,7 +41,7 @@ python3.12 -m venv .venv
.venv/bin/ecp6 --config config/ecp6.yaml validate
```
-Expected baseline: 125 passing tests and split sizes `14336/1024/1024`.
+Expected baseline: 127 passing tests and split sizes `14336/1024/1024`.
## 4. Reproduce ECP-6
@@ -71,7 +71,7 @@ This reruns a known experiment. It must not be presented as a new independent co
## 5. Continue ECP-7 scientifically
-Do not modify ECP-0 through ECP-6 or the completed ECP-7 Batch 1 through Batch 26
+Do not modify ECP-0 through ECP-6 or the completed ECP-7 Batch 1 through Batch 27
configs. ECP7-B1-I collapsed to 130–139 hard messages. ECP7-B2-I improved its
soft objective but collapsed further to 85–104 hard messages. ECP7-B3-I applied
that loss to straight-through hard messages and improved to 585–972 messages,
@@ -155,11 +155,17 @@ and translator validation, but its unique-message counts were effectively
unchanged. Shared failures fell while any-link failures rose, closing the
replay-routing family as an error-redistribution mechanism rather than an
injectivity solution. The confirmatory ECP-7 test remains sealed.
+ECP7-B27-I added a late bounded direct collision-pair penalty. It reached
+85.38% mean validation and reduced collision-pair multiplicity, but produced
+exactly the same 13,440, 12,960, 12,720 and 13,440 unique-message counts as
+Batch 25. Worst-link validation regressed to 82.13%, so the existing collision
+loss is also closed as a capacity solution. The confirmatory test remains
+sealed.
Continue with this sequence:
1. Read `docs/research-design-ecp7.md` and `docs/development-log-ecp7.md`.
-2. Define exactly one ECP7-B27 direct sender-capacity intervention and its
+2. Define exactly one ECP7-B28 sender-representation or occupancy intervention and its
failure criterion.
3. Register the variant and immutable configuration hashes before training.
4. Add tests for every new invariant.
@@ -173,20 +179,20 @@ Continue with this sequence:
## 6. Recommended next experiment
-Batch 26 shows that another one-sided replay cycle cannot increase sender code
-capacity. Receiver-only phases fit the existing codebook ceiling; sender-only
-ordinary-task replay converts shared errors into link-specific errors. Do not
-try another route boundary, route cycle or horizon extension.
-
-For ECP7-B27, preregister exactly one direct sender-capacity intervention on the
-strongest stable base. A defensible candidate is a late, bounded pulse that
-explicitly separates globally mined hard-message collisions while preserving
-the ordinary reconstruction task. Reuse an already tested loss and coefficient
-where possible, activate it only after a shared unchanged trajectory, and
-define success primarily as a reproducible increase in unique messages without
-loss of worst-link validation. Do not combine this with a new architecture,
-new predicate, optimizer change, factor-specific supervision, variable-length
-messages or negotiation in the same batch.
+Batch 27 shows that the existing relaxed collision-pair loss can reduce
+collision multiplicity but cannot create newly occupied hard codes. Do not try
+another coefficient, pulse boundary, route cycle or horizon extension.
+
+For ECP7-B28, preregister exactly one factor-agnostic sender representation or
+genuinely different occupancy-creation intervention. A defensible
+representation candidate is an identity-preserving residual interaction block
+on the successful bounded-parallel sender: retain the complete shallow joint
+context and slot heads, add one shared nonlinear residual branch initialized to
+zero output, and change no receiver, loss, data or training schedule. This
+isolates whether missing joint-context interaction capacity caused the stable
+code ceiling while avoiding Batch 13's replacement of the successful shallow
+path. Do not combine it with collision replay, a new coefficient, factor-slot
+bindings, optimizer changes, variable-length messages or negotiation.
## 7. Definition of done for any contribution
diff --git a/docs/development-log-ecp7.md b/docs/development-log-ecp7.md
index f7c9bf5..f23dc03 100644
--- a/docs/development-log-ecp7.md
+++ b/docs/development-log-ecp7.md
@@ -34,6 +34,7 @@ this log.
| ECP7-B24-I | Intervention | B23 sender-only warmup followed by receiver-only replay catch-up | Position-aware MLP | valid negative; new-best means but link-specific errors and injectivity fail |
| ECP7-B25-I | Intervention | B24 receiver-only catch-up with a 45,000-step maximum horizon | Position-aware MLP | valid negative; new-best means reach the non-injective code-cap |
| ECP7-B26-I | Intervention | B25 with a second sender-only pulse and final receiver-only catch-up | Position-aware MLP | valid negative; route cycle changes error sharing but not code capacity |
+| ECP7-B27-I | Intervention | B25 plus one late bounded direct sender-collision pulse | Position-aware MLP | valid negative; collision multiplicity falls but unique-code capacity is unchanged |
## Batch 1 preregistration
@@ -2184,3 +2185,106 @@ sender-only training reduces shared errors by fragmenting them across links.
The ECP-7 confirmatory split remains sealed. Any future batch must change the
sender collision objective or sender representation directly, rather than add
another route phase, route boundary or horizon extension.
+
+## Batch 27 preregistration
+
+Preregistered: July 18, 2026 at 18:05:30 UTC
+Seed: `11`
+Maximum population steps: `45,000`
+Minimum selection steps: `15,000`
+Early-stopping patience: `5,000` steps
+Base hard-meaning replay: unchanged B25 sender-only warmup, then receiver-only catch-up
+Direct sender-collision pulse start: step `30,000`, weight `0`
+Direct sender-collision pulse peak: step `35,000`, weight `0.1`
+Direct sender-collision pulse end: step `40,000`, weight `0`
+Collision pairs: globally mined hard-message collisions per sender on training meanings only
+Collision pair batch: `32` pairs per sender, uniform with replacement
+Collision refresh interval: `200` steps
+Relaxed replay temperature: `1.0`
+Raw configuration SHA-256: `d44df83436f5ca81a2a51229b8ffe1a6fd6832d28dff4c7ab2ead3ace8efc9bd`
+Effective configuration SHA-256: `649ddca4a285fbbb00b29c1cab82678e42ab8d6810f89f2131766cd82496c463`
+Split-SHA-256: `4947058c75ab07cb43a87eb82776b12cb2a7e2eeba7114de110d3b852cbc64cd`
+Test unsealed: **no**
+
+ECP7-B27-I inherits Batch 25 and adds only one bounded direct sender-collision
+loss after the unchanged trajectory through step 30,000. It reuses Batch 18's
+task-scale `0.1` coefficient and Batch 19's registered 5,000-step rise and
+5,000-step decay. The existing shared-hard-meaning replay remains receiver-only
+after step 20,000, so readers continue fitting while the new branch acts only
+on sender code separation. A final 5,000-step zero-collision-loss phase permits
+unperturbed receiver catch-up.
+
+The positive control is rerun. All existing validity and development gates
+remain unchanged. The direct-capacity hypothesis also requires a selected
+minimum sender codebook larger than Batch 25's 12,720-message minimum without a
+lower worst-link validation score. No alternative coefficient, pulse boundary,
+pair sampler, replay route, predicate, architecture, optimizer, horizon, data,
+translator or seed may be tried inside Batch 27. Failure is recorded without
+confirmatory test access.
+
+## Batch 27 results
+
+Positive-control run: `runs/ecp7-batch27-control-development/20260718T180724Z-ecp7-development`
+Intervention run: `runs/ecp7-batch27-intervention-development/20260718T180838Z-ecp7-development`
+Test unsealed: **no**
+
+| Metric | Positive control | ECP7-B27-I |
+|---|---:|---:|
+| Population train, mean | 100% | **85.3760%** |
+| Population train, worst link | 100% | 82.1498% |
+| Population validation, mean | 100% | **85.3760%** |
+| Population validation, worst link | 100% | 82.1289% |
+| Universal translator, validation | 100% | **85.3516%** |
+| Exact sender-message agreement | 100% | 92.36% |
+| Unique messages per sender | 15,360 | 12,720–13,440 |
+| Collision meanings per sender | 0 | 1,920–2,640 |
+| Message entropy | 13.91 bits | 13.56–13.66 bits |
+| Development gate | pass | **fail** |
+
+The positive control passed every gate at 100%. ECP7-B27-I modestly exceeds
+Batch 25's mean train, mean validation and translator scores, but fails both
+train thresholds, sender injectivity and the preregistered direct-capacity
+criterion. Worst-link validation falls from Batch 25's 82.7148% to 82.1289%.
+The joint gate fails and confirmatory access remains unauthorized.
+
+All 151 optimization records through step 30,000 match Batch 25 exactly after
+excluding the new zero-weight collision diagnostics. The first mining boundary
+finds 2,429, 2,578, 2,912 and 2,128 unordered collision pairs. At the pulse
+peak on step 35,000, the pair banks contain 1,792, 2,605, 2,480 and 2,128
+pairs. Once the pulse reaches zero at step 40,000 they stabilize at 1,792,
+2,240, 2,464 and 1,792 pairs through the selected step 43,800 and final step
+45,000.
+
+For comparison, Batch 25's selected hard codebooks contain 2,128, 2,576,
+2,912 and 2,128 unordered collision pairs. B27 therefore reduces collision
+pair multiplicity substantially. It nevertheless produces exactly the same
+unique-message counts as B25: 13,440, 12,960, 12,720 and 13,440. Collision
+meaning counts are likewise unchanged at 1,920, 2,400, 2,640 and 1,920. The
+higher 13.56–13.66-bit entropies show that the loss redistributes multiplicity
+among already occupied codes instead of creating new occupied codes.
+
+The selected diagnostic finds 1,792 meanings failed by all links and 2,812
+failed by at least one link, with 11.93 mean failed links per erroneous
+meaning. Relative to Batch 25, any-link failures fall by 155 while shared
+failures rise by 115. The direct pulse therefore produces a different
+error-sharing tradeoff from route cycling, but still cannot raise the minimum
+sender code capacity above 12,720.
+
+Population validation factor accuracies were
+`[100%, 100%, 98.44%, 86.94%]`; universal-translator factor accuracies were
+`[100%, 100%, 98.44%, 86.91%]`. Both sealed analyses report 65 matching
+artifact hashes, 16,384 validation-only episodes, no confirmatory-test keys,
+valid local alphabets, and exactly 14 declared bits for every logged message.
+
+## Batch 27 decision
+
+ECP7-B27-I is a valid negative result. Direct relaxed collision-pair pressure
+can reduce high-multiplicity hard collisions while preserving mean accuracy,
+but it does not occupy additional codes and it weakens the worst population
+link. Repeating this loss with another late coefficient or pulse boundary is
+not justified.
+
+The ECP-7 confirmatory split remains sealed. A future batch must change the
+factor-agnostic sender representation or introduce a genuinely different
+occupancy-creation mechanism; it may not combine that change with another
+replay-route or collision-coefficient search.
diff --git a/docs/research-design-ecp7.md b/docs/research-design-ecp7.md
index a4f2f4b..1217686 100644
--- a/docs/research-design-ecp7.md
+++ b/docs/research-design-ecp7.md
@@ -1147,3 +1147,61 @@ train and injectivity gates without accessing the confirmatory split.
This completes the registered replay-routing family. Further route cycling or
horizon extension is disallowed as an unsupported repetition; any later
development batch must preregister a direct sender-capacity intervention.
+
+## Batch 27 preregistration — Late bounded sender-collision pulse
+
+Status: **completed sealed development; valid negative**
+Preregistered: July 18, 2026 at 18:05:30 UTC
+Configuration: `config/ecp7-b27-development.yaml`
+Raw configuration SHA-256: `d44df83436f5ca81a2a51229b8ffe1a6fd6832d28dff4c7ab2ead3ace8efc9bd`
+Effective configuration SHA-256: `649ddca4a285fbbb00b29c1cab82678e42ab8d6810f89f2131766cd82496c463`
+
+Batch 25 establishes the strongest stable receiver-saturated base. Batch 26
+shows that ordinary-task route cycling cannot add sender code capacity. Batch
+27 therefore changes the objective rather than the route: it directly
+penalizes each sender's globally mined hard-message collisions only after the
+complete Batch 25 trajectory through step 30,000.
+
+ECP7-B27-I inherits the full Batch 25 configuration and implementation. The
+sole addition is the existing factor-agnostic global collision-pair mechanism
+with this fixed schedule:
+
+1. weight zero through step 30,000;
+2. linear warmup to `0.1` at step 35,000;
+3. linear decay to zero at step 40,000;
+4. weight zero through the 45,000-step ceiling.
+
+Pairs are mined independently for every sender from hard messages over training
+meanings only, refreshed every 200 steps and sampled as 32 pairs per sender
+with replacement. Their relaxed full-message collision probability is measured
+at temperature `1.0`. The coefficient is reused from Batch 18; the 5,000-step
+rise and decay durations are reused from Batch 19. The unchanged hard-meaning
+replay branch stays receiver-only after step 20,000, and the ordinary task
+continues to update both sides.
+
+The batch contains exactly two seed-11 runs:
+
+1. the unchanged ECP-6 positive control;
+2. ECP7-B27-I with the late bounded sender-collision pulse.
+
+All existing validity and development gates remain unchanged. Additionally,
+the direct-capacity claim requires the selected minimum unique-message count to
+exceed Batch 25's 12,720 while worst-link validation is at least Batch 25's
+82.7148%. No alternative coefficient, pulse boundary, pair sampler, replay
+route, hard-meaning predicate, architecture, base loss, optimizer, horizon,
+data, translator or seed may be tried inside Batch 27. Failure is recorded
+without confirmatory test access.
+
+### Batch 27 outcome
+
+The registered seed-11 intervention reached 85.38% mean train and validation,
+82.15% worst-link train, 82.13% worst-link validation and 85.35% universal-
+translator validation. Its global collision-pair banks became substantially
+smaller and message entropy increased, but sender codebooks still contained
+exactly 13,440, 12,960, 12,720 and 13,440 unique messages—the same capacities
+as Batch 25. It failed the unchanged train and injectivity gates and the added
+minimum-capacity/worst-link criterion without accessing the confirmatory split.
+
+This closes coefficient and timing development for the existing relaxed
+collision-pair loss. Any later batch must preregister a distinct sender
+representation or occupancy-creation mechanism.
diff --git a/evidence/ecp7-development/README.md b/evidence/ecp7-development/README.md
index 94f2a7f..deed4b3 100644
--- a/evidence/ecp7-development/README.md
+++ b/evidence/ecp7-development/README.md
@@ -151,6 +151,13 @@ rose from 2,967 to 3,262, confirming that route cycling redistributes errors
without fixing sender collisions. Exact identities and hashes are in
`batch26-manifest.json`.
+Batch 27 added a late bounded direct penalty on globally mined sender
+collisions. It reduced unordered collision-pair multiplicity and increased
+message entropy, but produced exactly the same unique-message counts as Batch
+25 while worst-link validation fell to 82.13%. The existing collision loss
+therefore reshapes occupied codes without creating new ones. Exact identities
+and hashes are in `batch27-manifest.json`.
+
Reproduce the development arms with:
```bash
diff --git a/evidence/ecp7-development/batch27-manifest.json b/evidence/ecp7-development/batch27-manifest.json
new file mode 100644
index 0000000..3cea7d0
--- /dev/null
+++ b/evidence/ecp7-development/batch27-manifest.json
@@ -0,0 +1,55 @@
+{
+ "batch": "ECP7-B27",
+ "confirmatory_test_unsealed": false,
+ "config_effective_sha256": "649ddca4a285fbbb00b29c1cab82678e42ab8d6810f89f2131766cd82496c463",
+ "config_raw_sha256": "d44df83436f5ca81a2a51229b8ffe1a6fd6832d28dff4c7ab2ead3ace8efc9bd",
+ "control": {
+ "artifact_hashes": {
+ "effective_config.yaml": "4bfb976293f117d3ae35f790e4fd6371628be95d91febb334bc275dc5d6b66d4",
+ "posthoc-analysis.json": "c52382de54ed1ae11716256fa3c9a48f9e61cd6ef39d927ef4fba93548938d0b",
+ "seed-11/metrics.json": "d772d990448d4f2450cf2b2fed901ab45acec4f74d6b24a9c060e5c0748c48fc",
+ "summary.json": "0b4f13ef92858adf9113a022fe0d65679f95dfa0d55d959d2b366c9910f19efe"
+ },
+ "development_gate_passed": true,
+ "population_train_mean_exact_match": 1.0,
+ "population_validation_mean_exact_match": 1.0,
+ "run_id": "20260718T180724Z-ecp7-development",
+ "universal_translator_validation_exact_match": 1.0,
+ "unique_messages_per_sender": [15360, 15360, 15360, 15360]
+ },
+ "intervention": {
+ "artifact_hashes": {
+ "effective_config.yaml": "87db10e18624e99741de1063cbbc0198ef88aed76472ddbfcbdf0843f90f7a48",
+ "posthoc-analysis.json": "79146dee4f0b1c41c64553fb4c5eddd832726e8946b30ff6375db810f7285ce7",
+ "seed-11/metrics.json": "97f600d564f387425027151cfe4b534260819975e54920676a00177e48dc8200",
+ "summary.json": "967378da477f8537b84a5ec23c590c961145f2d464776d8ac54abd57c78fa406"
+ },
+ "batch25_collision_pairs_per_sender": [2128, 2576, 2912, 2128],
+ "best_step": 43800,
+ "collision_meanings_per_sender": [1920, 2400, 2640, 1920],
+ "collision_pairs_per_sender": [1792, 2240, 2464, 1792],
+ "development_gate_passed": false,
+ "exact_sender_message_agreement": 0.9236002604166668,
+ "final_hard_meaning_replay_pool_size": 1792,
+ "final_step": 45000,
+ "initial_hard_meaning_replay_pool_size": 1745,
+ "message_entropy_bits_per_sender": [13.656890595608516, 13.594390595608516, 13.563140595608516, 13.656890595608516],
+ "minimum_hard_meaning_replay_pool_size": 1414,
+ "minimum_hard_meaning_replay_pool_step": 18200,
+ "population_train_mean_exact_match": 0.853759765625,
+ "population_train_worst_link_exact_match": 0.8214983258928571,
+ "population_validation_factor_accuracy": [1.0, 1.0, 0.984375, 0.869384765625],
+ "population_validation_mean_exact_match": 0.853759765625,
+ "population_validation_worst_link_exact_match": 0.8212890625,
+ "run_id": "20260718T180838Z-ecp7-development",
+ "selected_all_links_wrong_meaning_count": 1792,
+ "selected_any_link_wrong_meaning_count": 2812,
+ "selected_mean_incorrect_links_per_hard_meaning": 11.928875923156738,
+ "universal_translator_validation_exact_match": 0.853515625,
+ "unique_message_capacity_mean": 0.85546875,
+ "unique_messages_per_sender": [13440, 12960, 12720, 13440]
+ },
+ "seed": 11,
+ "split_artifact_sha256": "676195997ee0c77d5c9565bae045dbb938b432a2e4e74b666233b0a41f924718",
+ "split_sha256": "4947058c75ab07cb43a87eb82776b12cb2a7e2eeba7114de110d3b852cbc64cd"
+}
diff --git a/tests/conftest.py b/tests/conftest.py
index efceb99..b319e22 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -173,3 +173,8 @@ def ecp7_b25_config():
@pytest.fixture(scope="session")
def ecp7_b26_config():
return load_config(PROJECT_ROOT / "config" / "ecp7-b26-development.yaml")
+
+
+@pytest.fixture(scope="session")
+def ecp7_b27_config():
+ return load_config(PROJECT_ROOT / "config" / "ecp7-b27-development.yaml")
diff --git a/tests/test_config.py b/tests/test_config.py
index 854b7f3..47bfb1f 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -943,3 +943,30 @@ def test_hard_replay_gradient_route_phases_are_bounded_and_one_sided(
][-1]["end_step"] = 44800
with pytest.raises(ConfigError, match="must cover max_steps"):
validate_config(invalid_final)
+
+
+def test_ecp7_b27_adds_only_a_late_bounded_collision_pulse(
+ ecp7_b25_config, ecp7_b27_config
+):
+ assert ecp7_b27_config["world"] == ecp7_b25_config["world"]
+ assert ecp7_b27_config["dataset"] == ecp7_b25_config["dataset"]
+ assert ecp7_b27_config["channel"] == ecp7_b25_config["channel"]
+ assert ecp7_b27_config["agents"] == ecp7_b25_config["agents"]
+ b25_training = deepcopy(ecp7_b25_config["training"])
+ b27_training = deepcopy(ecp7_b27_config["training"])
+ assert b27_training.pop("global_collision_replay") == {
+ "enabled": True,
+ "weight": 0.1,
+ "start_step": 30000,
+ "warmup_steps": 5000,
+ "pair_batch_size": 32,
+ "refresh_interval": 200,
+ "relaxed_temperature": 1.0,
+ "weight_decay": {
+ "enabled": True,
+ "start_step": 35000,
+ "end_step": 40000,
+ "final_weight": 0.0,
+ },
+ }
+ assert b27_training == b25_training
diff --git a/tests/test_training.py b/tests/test_training.py
index 37602f3..cca1e34 100644
--- a/tests/test_training.py
+++ b/tests/test_training.py
@@ -731,6 +731,19 @@ def test_hard_replay_coordinate_route_follows_registered_phases(
)
+def test_late_sender_collision_pulse_preserves_the_first_30000_steps(
+ ecp7_b27_config,
+):
+ schedule = ecp7_b27_config["training"]["global_collision_replay"]
+
+ assert _scheduled_collision_replay_weight(schedule, 30000) == 0.0
+ assert _scheduled_collision_replay_weight(schedule, 32500) == 0.05
+ assert _scheduled_collision_replay_weight(schedule, 35000) == 0.1
+ assert _scheduled_collision_replay_weight(schedule, 37500) == 0.05
+ assert _scheduled_collision_replay_weight(schedule, 40000) == 0.0
+ assert _scheduled_collision_replay_weight(schedule, 45000) == 0.0
+
+
def test_receiver_binding_calibration_recovers_exact_permutation(ecp4_config):
receiver = FactorizedPermutationSlotReceiver(ModelSpec.from_config(ecp4_config))
meanings = torch.tensor(