cella_gui 1.0.0

A GUI for the Cella cellular automata library
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
# E30a — the arrival-time kernel on a flat grid · finding — a diagonal-cost bug doubled the diagonal-vs-cardinal step ratio and explained most, but not all, of rear-focus's overshoot; fixed, the kernel now nearly matches Anderson's LB(U) at the ensemble's real operating wind, with a smaller, understood hull-geometry residual left at high wind. Separately, the exponential law's own "closed form" (`cosh(c2·v)`) was a controller error from fix round 1: correctly derived, the law's template is nearly round at every tested wind and the model matches its own (much smaller) correct target within 3 %

_Round 6 (2026-09-11/12) · fix round 5 (exponential-law closed form corrected, a controller error found in review), fix round 4 (diagonal-cost double-count found and fixed), fix round 2/3 (upwind-ignition domain + mechanism explanation, now superseded by the code fix), fix round 1 (centred 400×400, boundary-limited, superseded) and v1 (heat accumulator, superseded) kept below for the record · 3 seeds · synthetic grids, no fire · example `cella_lib/examples/wildfire_ros.rs` (`arrival_flat`, `illuminate`, `lb`, `head_speed` modes) · results `exp30a_arrival_flat.json` · figure [figures/e30a-arrival-flat.svg](figures/e30a-arrival-flat.svg) · terms: [GLOSSARY.md](GLOSSARY.md)_

## v2b, fix rounds 4–5 — the diagonal-cost fix and the corrected exponential closed form (the current main result)

**In short.** A full-diff review of this task found that `step_chunk_arrival`'s
travel cost multiplied an extra `norm_j` (1 cardinal, `√2` diagonal) into
`cost_j` on top of the `1/norm_j` diagonal-distance correction already
built into `dir[j]`, squaring the diagonal-vs-cardinal cost ratio: a
diagonal step cost `2×` a cardinal step instead of the correct `√2×`, so
the arrival rule's diagonal effective speed was `0.71×` cardinal instead
of equal to it — an anisotropic kernel at *every* wind speed, including
calm wind, where fix rounds 2–3 had no other direction signal to notice
it against. Fixed (drop the extra `norm_j` factor: `cost_j = jitter /
(p_base · dir[j] · slope)`) and covered by a new isotropy test. Every
table below is rebuilt from the fixed, committed binary. The upshot:
**most of what fix round 3 called "rear-focus's overshoot" was this bug,
not the 8-direction hull effect it described** — the fix removes roughly
half to nearly all of the measured overshoot at every template point,
and at the ensemble's actual operating wind (LB ≤ ~1.3) the remaining,
real hull effect is now close to **zero**, not the previously-stated
"≤ ~20 %". A smaller but still substantial hull-geometry residual
remains at high wind (LB ≈ 7: +235 % instead of the old +373 %), and the
mechanism fix round 3 described for it is still correct — only its
numbers, which had the bug baked in, needed replacing.

**Result — Table 1: flat-grid front speed (cells/tick), both rules,
burn duration 5, wind law exponential** (rebuilt from the fixed binary;
Bernoulli is byte-identical to every prior round, confirming the fix
touched nothing on that path):

| p0 | Bern 0 | Bern 2 | Bern 5 | Bern 8 | Arr 0 | Arr 2 | Arr 5 | Arr 8 |
|---|---|---|---|---|---|---|---|---|
| 0.12 | 0.4744 | 0.4770 | 0.4965 | 0.5154 | 0.1290 | 0.1394 | 0.1571 | 0.1776 |
| 0.22 | 0.7009 | 0.7085 | 0.7278 | 0.7549 | 0.2366 | 0.2556 | 0.2881 | 0.3257 |
| 0.44 | 0.9598 | 0.9683 | 0.9830 | 0.9937 | 0.4732 | 0.5113 | 0.5763 | 0.6509 |

Arrival's numbers moved up a little from fix round 1's own Table 1
(p0 = 0.12: `0.123/0.134/0.153/0.174` → `0.129/0.139/0.157/0.178`, a
2–5 % rise, largest at low wind) even though this measurement's own
front only ever advances along the cardinal (+x) direction, where
`cost_cardinal` (`norm_j = 1`) is unchanged by the fix. The shift is a
real, second-order boundary effect, not noise: `front_x` averages the
rightmost burning cell over *every* row, including the top/bottom edge
rows, which have fewer neighbours and so let a small amount of
diagonal-path "leakage" into their own relaxation — cheaper diagonal
steps under the fix (`√2×` cardinal instead of `2×`) let those edge
rows catch up very slightly faster, nudging the whole-grid average.
`head_speed`'s own jitter-0 measurement (below) confirms the underlying
cardinal *rate* is unaffected (matches the closed form within 1 % both
before and after this fix), so this is a boundary-relaxation detail, not
a rate-law change. Proportionality to `p0` is preserved exactly
(`0.2366/0.1290 = 1.834 ≈ 0.22/0.12`), as before.

**Result — head speed vs. the closed form** (arrival rule, jitter 0,
p0 = 0.12, rebuilt):

| wind | law | measured (cells/tick) | closed form `p0·exp(c1·v)` | ratio | boundary contact |
|---|---|---|---|---|---|
| 0 m/s | exponential | 0.1208 | 0.1200 | 1.007 | yes (west edge only) |
| 0 m/s | rear_focus | 0.1208 | 0.1200 | 1.007 | yes (west edge only) |
| 2 m/s | exponential | 0.1315 | 0.1313 | 1.002 | yes (west edge only) |
| 2 m/s | rear_focus | 0.1314 | 0.1313 | 1.001 | no |
| 5 m/s | exponential | 0.1491 | 0.1503 | 0.992 | no |
| 5 m/s | rear_focus | 0.1501 | 0.1503 | 0.999 | no |
| 8 m/s | exponential | 0.1716 | 0.1720 | 0.998 | no |
| 8 m/s | rear_focus | 0.1693 | 0.1720 | 0.984 | no |

Every row matches the closed form within 1.6 % — including rear-focus
at 8 m/s, which fix round 2 flagged as boundary-contaminated at
0.668×; the fixed diagonal cost lets the head advance fast enough that
the far edge is no longer reached before the second checkpoint at this
wind, so this row is now clean, unlike every prior round's version of
this table.

**Result — Table 2: elongation vs. burned-cell count (mean of 3 seeds,
jitter 0.2, p0 = 0.12), arrival rule, both wind laws, four winds**
(rebuilt; `*` = boundary contact; Bernoulli rows, unaffected by this
fix, are byte-identical to fix round 2's table and omitted here — see
the superseded section below for them):

| wind | law | 2,000 | 5,000 | 10,000 | 20,000 |
|---|---|---|---|---|---|
| 0 m/s | exponential | 1.024 | 1.023* | 1.178* | 1.375* |
| 0 m/s | rear_focus | 1.024 | 1.023* | 1.178* | 1.375* |
| 2 m/s | exponential | 1.021 | 1.024 | 1.022* | 1.147* |
| 2 m/s | rear_focus | 1.435 | 1.434 | 1.442 | 1.444 |
| 5 m/s | exponential | 1.027 | 1.025 | 1.023 | 1.021 |
| 5 m/s | rear_focus | 4.115 | 4.178 | 4.193 | 4.189 |
| 8 m/s | exponential | 1.072 | 1.070 | 1.073 | 1.071 |
| 8 m/s | rear_focus | 16.108 | 16.447 | 16.729 | 16.731 |

Still flat with size at every wind and law (the self-similarity finding
survives the fix unchanged, as expected — the Minkowski-self-sum
argument for *why* it's flat never depended on the exact per-direction
cost values, only on the cost polygon being fixed shape). What changed
is the *level*: exponential's elongation roughly halved (e.g. 8 m/s:
`1.308` → `1.071`) and rear-focus's dropped by a third to a half
(2 m/s: `1.818` → `1.444`; 8 m/s: `23.09` → `16.73`).

**Result — Table 3: length-to-breadth vs. Anderson's `LB(U)`, arrival
rule, p0 = 0.12, both jitter settings, 10,000-cell checkpoint unless
starred** (rebuilt; `*` = boundary contact):

| wind | law | c2 | LB (jitter 0.2) | /Anderson | LB (jitter 0) | /Anderson | Anderson |
|---|---|---|---|---|---|---|---|
| 0.9690 m/s | rear_focus (template LB 1.2) | — | n/a (jitter 0 only) | — | 1.190 | **0.99** | 1.200 |
| 2 m/s | exponential | 0.131 | 1.147* | 0.76 | 1.135* | 0.75 | 1.505 |
| 2 m/s | exponential | 0.45 | 1.049 | 0.70 | 1.068 | 0.71 | 1.505 |
| 2 m/s | rear_focus | — | 1.442 | 0.96 | 1.524 | 1.01 | 1.505 |
| 3.1771 m/s | rear_focus (template LB 2.0) | — | n/a (jitter 0 only) | — | 2.290 | **1.14** | 2.000 |
| 5 m/s | exponential | 0.131 | 1.023 | 0.32 | 1.034 | 0.32 | 3.192 |
| 5 m/s | exponential | 0.45 | 1.359 | 0.43 | 1.462 | 0.46 | 3.192 |
| 5 m/s | rear_focus | — | 4.193 | 1.31 | 5.174 | 1.62 | 3.192 |
| 8 m/s | exponential | 0.131 | 1.073 | 0.15 | 1.096 | 0.16 | 7.028 |
| 8 m/s | exponential | 0.45 | 1.883 | 0.27 | 2.145 | 0.31 | 7.028 |
| 8 m/s | rear_focus | — | 16.729 | 2.38 | 23.553 | 3.35 | 7.028 |

**The template-LB table, before and after the fix (five points, jitter
0, p0 = 0.12, 10,000-cell checkpoint, rear-focus)** — this is the number
the controller specifically asked for: how much of the previously
measured overshoot was the bug, and how much is the real hull effect
that remains.

| template LB | wind (m/s) | Anderson | measured, buggy (fix round 3) | overshoot, buggy | measured, fixed | overshoot, fixed | overshoot removed |
|---|---|---|---|---|---|---|---|
| 1.2 | 0.9690 | 1.200 | 1.3976 | +16.5 % | 1.1901 | **−0.8 %** | 17.3 pts |
| 1.5 | 2 | 1.505 | 2.0083 | +33.4 % | 1.5239 | **+1.3 %** | 32.2 pts |
| 2.0 | 3.1771 | 2.000 | 3.1818 | +59.1 % | 2.2897 | **+14.5 %** | 44.6 pts |
| 3.2 | 5 | 3.1922 | 7.3275 | +129.5 % | 5.1736 | **+62.1 %** | 67.5 pts |
| 7.0 | 8 | 7.0278 | 33.2527 | +373.2 % | 23.5527 | **+235.1 %** | 138.1 pts |

Two things stand out. First, **the bug was the majority of the
"overshoot" at every point** — it accounts for more than half the
error at LB 1.2–2.0, and still a large minority even at LB 7 (138 of
373 points). Second, **the residual after the fix is close to zero at
low LB and grows the same way the bug did**: essentially unbiased at
LB 1.2 (a hair under Anderson), a genuine +1 % to +15 % at LB 1.5–2.0,
and a real, substantial +62 % to +235 % at LB 3.2–7. That growth
pattern — small at low eccentricity, large at high eccentricity — is
exactly what fix round 3's mechanism (an 8-direction grid can't sample
a needle-thin ellipse finely enough) predicts, and it is what remains
once the unrelated bug is removed: **the mechanism was real, its
numbers were not.**

**Closed-form check (jitter 0), CORRECTED in fix round 5: exponential,
c2 = 0.131, 8 m/s: measured 1.096 vs. the correct closed form 1.065 —
2.9 % short, comfortably within 15 %.** `cosh(c2·v) = 1.601` (fix round
1's original formula, repeated in every round through fix round 4) is
a **controller error found in review**, not a property of the model:
it assumed the shape's half-width is set by the 90°-flank rate
`e^-c2v`, but the minimum-travel-time shape is the polar curve
`r(θ) = e^(c2·v·(cosθ−1))` (relative to the head), and a polar curve's
true lateral half-width is `max_θ [r(θ)·sinθ]` — not its value at
exactly θ = 90°. At c2·v = 1.048 that maximum falls near θ ≈ 50°
(`r(50°)·sin(50°) ≈ 0.527`), not at 90° (`r(90°) = e^-1.048 ≈ 0.351`),
giving the correct closed form `LB = (1 + e^-2c2v) / (2 · 0.527) ≈
1.065` — the exponential law's template is **nearly round** at 8 m/s,
which is the actual, corrected finding: fix round 4's "31.5 % short"
was measuring the model against the wrong target, not finding a real
shortfall. This is also the quantitative reason E19/E37 found this law
produces so little shape. The unit test's bound is restored to 15 %
against the corrected closed form (computed in the test itself by a
1°-step scan over θ, not hard-coded). Rear-focus, 2 m/s: measured 1.524
(jitter 0) vs.
Anderson 1.505 — **1.3 % over**, and the 30 %-bound unit test (default
jitter, mean of 3 seeds) now measures **0.958× (4.2 % under)**, a huge
improvement on fix round 3's ~20 % over. Rear-focus, 5 m/s: measured
5.174 vs. Anderson 3.192 — 62 % over (was 130 %), still confirms
*over*, not *under* (the direction-only unit test).

**Prediction check**, against the v1.8/fix-round-2 predictions restated
for this round (elongation flat with size at every wind for both laws;
jitter-0 LB equals the exponential law's closed form within 15 %, and
is within 20 % of Anderson under rear-focus, at 2/5/8 m/s; p0 0.12
fires no longer die — the original prediction text named `cosh(c2·v)`
as that closed form, now known to be a controller error; see the
corrected-closed-form check above):

1. *Elongation flat with size, both laws.* **Still confirmed**, at the
   new (lower) levels shown in Table 2.
2. *Jitter-0 exponential LB within 15 % of its own closed form,
   c2 = 0.131, 8 m/s.* **Confirmed against the corrected closed form**
   (2.9 % short of 1.065) — it only looked refuted (31.5 % short, fix
   round 4) against the wrong target, `cosh(c2·v) = 1.601`. At 5 m/s:
   measured 1.034 vs. corrected closed form 1.017 — 1.7 % short, also
   comfortably confirmed.
3. *Jitter-0 rear-focus LB within 20 % of Anderson at 2/5/8 m/s.*
   **Refuted at 5 and 8 m/s** (62 % and 235 % over), **now confirmed at
   2 m/s** (1 % over — it was 33 % over under the bug). The picture at
   the low-wind end this model actually operates in is now much better
   than fix round 3 reported.
4. *p0 0.12 fires no longer die.* **Still confirmed** for arrival;
   unaffected by this fix.

**What it means.**

1. **Most of what looked like a geometric limitation was a bug.**
   Fix round 3's mechanism (an 8-direction grid samples a needle, not
   the ellipse) is real and still explains the *shape* of the residual
   error — but its *size* was inflated by a factor of roughly 2–4× by
   the diagonal-cost double-count, most severely at low eccentricity
   (17 points removed at LB 1.2 out of 16.5 total — i.e. almost all of
   it — versus 138 of 373 points at LB 7, where the genuine hull effect
   dominates even without the bug).
2. **At the ensemble's real operating wind, the kernel is now
   essentially unbiased, not merely "within ~20 %."** The wildfire
   ensemble's `wind_scale` gene (range `[0, 1.5]`,
   `cella_lib/src/explore/genome.rs`) times the six fires' ERA5 wind
   (0.5–0.7 m/s) puts the practical wind ceiling at `0.7 × 1.5 = 1.05`
   m/s (`anderson_lb ≈ 1.22`) and floor at `0.5 × 1.5 = 0.75` m/s
   (`anderson_lb ≈ 1.15`) — interpolating the template table between
   LB 1.2 (−0.8 %) and LB 1.5 (+1.3 %) puts the *actual* overshoot the
   ensemble ever experiences at **roughly 0 %**, down from the
   previously-stated "≤ ~20 %."
3. **A real, substantial hull-geometry residual remains at high wind**,
   and it is not this round's job to fix it: +62 % at LB ≈ 3.2, +235 %
   at LB ≈ 7. The two named-but-unimplemented real fixes (a finer
   angular neighbourhood; a fitted template-LB → realised-LB
   correction) are unchanged from fix round 3 and still the paths to
   take if Task 8 ever needs accuracy at higher wind.
4. **The exponential law was never undershooting anything — fix round
   1's own target formula was wrong.** `cosh(c2·v)` assumed the shape's
   half-width sits at the 90° flank; the true minimum-travel-time
   half-width is `max_θ [e^(c2·v·(cosθ−1))·sinθ]`, which at c2·v = 1.05
   falls near θ ≈ 50°, giving a *much* smaller closed-form LB (≈ 1.07,
   not 1.60). Measured against the correct target, the exponential law
   is within 3 % at 8 m/s and within 2 % at 5 m/s — its template is
   simply **nearly round** at every wind tested, by construction of
   the formula itself, not because the model fails to reproduce it.
   This is a controller error found in review (fix round 5), not a new
   measurement; no `c2` value is "recommended" because the law itself
   was never going to produce Anderson-like shape at any `c2` in the
   tested range — that conclusion still stands, just for the right
   reason now.
5. **The Bernoulli path is unaffected**, confirmed three ways: its own
   Table 2 rows are byte-identical to every prior round, the
   snapshot/hash stress tests in `long_suite.rs` pass unmodified, and
   the bug lived entirely inside `step_chunk_arrival`, a function
   Bernoulli's stepper never calls.

**Recommendation for E30 (Task 8), from the corrected measurement.**
Still **arrival**, still **rear_focus** for the front/back *sign* E41
needs — and now with a *better*, not merely a *bounded*, magnitude
claim at the wind range that matters: **validated to within ~5 % of
Anderson for LB ≤ 1.5** (was "≤ ~20 % over" before this fix), which
comfortably covers the ensemble's real operating ceiling (LB ≤ ~1.3,
overshoot ≈ 0 %). Do not extrapolate above LB ≈ 1.5 — the hull-geometry
residual is real, understood, and grows sharply (+62 % at LB ≈ 3.2,
+235 % at LB ≈ 7), with no tunable parameter to correct it short of the
two named structural fixes. No `c2` is recommended under the
exponential law — not because it undershoots some target (fix round 5
found its old target, `cosh(c2·v)`, was a controller error), but
because the law's own correct closed form shows its template is nearly
round at every tested wind by construction, so no `c2` in the tested
range produces Anderson-like elongation at all.

**Discipline.** Code (`35ee379`) committed, gated (`make clippy`,
`cargo test --release`, `cargo llvm-cov`), and rebuilt *before* any
measurement in this round ran; every number above comes from that exact
binary (`exp30a_arrival_flat.json`'s `binary_git` field), re-run in one
command via `exp_r6_arrival_flat.py` (extended this round to also carry
`head_speed`, so this is now a true one-command regeneration of the
whole file).

**Later.** E30 (Task 8, not yet run).

---

## v2 with the diagonal double-count (superseded 2026-09-12) — upwind-ignition domain, fix rounds 2–3

_Everything in this section (and its fix-round-3 addendum below) was
measured with a real bug in `step_chunk_arrival`: an extra `norm_j`
factor doubled the diagonal-vs-cardinal cost ratio instead of the
correct `√2×`. Fix round 4 found and fixed it; see the v2b section
above for the rebuilt numbers. The domain fix (upwind ignition,
absolute-cell-count checkpoints) and the self-similarity/rate findings
below are still correct and unaffected — only the magnitude numbers
changed. The needle/8-direction-hull *mechanism* fix round 3 describes
below is also still correct; only the numbers illustrating it (which
had the bug baked in) are superseded — see v2b's template-LB
before/after table for the corrected ones. Separately, every `cosh(c2·v)`
figure in this section (fix round 1's exponential-law "closed form")
is also wrong — a controller error found in fix round 5, not a
property of the model; see v2b's corrected-closed-form check above.
Kept verbatim for the record._

**In short.** Fix round 1 found that the arrival rule's elongation
"collapsed with size" under the rear-focus wind law — a result that
looked like a real property of the rule. It was a domain artefact. The
centred 400×400 grid put the boundary only 200 cells from the ignition
in every direction; at 8 m/s the fire's head has cost `1 / (p0 ·
exp(c1·v))` ≈ 5.8 ticks/cell (p0 = 0.12), so it reached that boundary at
tick ≈ 1,160 — almost exactly when an LB ≈ 7 shape's own area
(`π·200²/7` ≈ 18,000 cells) crosses the old 10 % checkpoint. Moving the
ignition upwind on an elongated 900×300 grid (860 cells of downwind
room instead of 200) and switching checkpoints to absolute burned-cell
counts with a `boundary_contact` flag removes that artefact entirely:
**elongation is now flat with size under both wind laws**, confirmed
directly by a new `head_speed` mode that matches the closed-form front
speed `p0 · exp(c1·v)` to within 1 % at every wind and law tested where
no boundary is involved. But the corrected measurement does not vindicate
either wind law's *magnitude*: the exponential law still falls further
below Anderson's `LB(U)` as wind rises (as fix round 1 found), while
rear-focus — once actually measured without the boundary contaminating
it — **overshoots** Anderson by a *larger* margin at higher wind (21 %
over at 2 m/s, growing to 230 % over at 8 m/s). The two laws bracket
Anderson's curve from opposite sides, and neither one reaches it.

**Why the fix mattered, precisely.** `illuminate`/`lb` now ignite a 3×3
patch at `x = 40` (not centred) on a 900 (x, along wind) × 300 (y) grid,
and read checkpoints as absolute burned-cell counts (2,000 / 5,000 /
10,000 / 20,000) rather than a fraction of the grid. Every checkpoint
carries a `boundary_contact` flag (any tracked cell within 2 cells of
any edge). This *doesn't* eliminate boundary contact everywhere — a
calm or mildly-elongated fire still grows enough in the *upwind*
direction to reach the `x = 40` edge at the larger checkpoints, and is
correctly flagged rather than silently trusted — but it removes it
specifically from the *downwind* measurements that matter for a
strongly wind-driven shape, which is what was contaminating fix round
1's numbers.

**Result — head speed vs. the closed form** (arrival rule, jitter 0,
p0 = 0.12; measured from the change in the fire's own downwind extent
between the 5,000- and 20,000-cell checkpoints):

| wind | law | measured (cells/tick) | closed form `p0·exp(c1·v)` | ratio | boundary contact |
|---|---|---|---|---|---|
| 0 m/s | exponential | 0.1201 | 0.1200 | 1.001 | yes (west edge only — irrelevant to this measurement) |
| 0 m/s | rear_focus | 0.1201 | 0.1200 | 1.001 | yes (west edge only) |
| 2 m/s | exponential | 0.1310 | 0.1313 | 0.998 | yes (west edge only) |
| 2 m/s | rear_focus | 0.1314 | 0.1313 | 1.001 | no |
| 5 m/s | exponential | 0.1508 | 0.1503 | 1.004 | yes (west edge only) |
| 5 m/s | rear_focus | 0.1502 | 0.1503 | 1.000 | no |
| 8 m/s | exponential | 0.1735 | 0.1720 | 1.009 | no |
| 8 m/s | rear_focus | 0.1150 | 0.1720 | **0.668** | **yes — downwind edge** |

Every row without *downwind* contact matches the closed form within 1 %
— direct confirmation that `cost = jitter · norm / (p_base · dir[j] ·
slope)` really does behave as a rate, exactly as designed. The one
exception (rear-focus, 8 m/s) is not a rate-semantics failure: the
front reaches the domain's far edge (x = 899 of 900) before the second
checkpoint, so the "speed" computed from it is an artefact of running
out of grid, correctly caught by the flag.

**Result — Table 2: elongation vs. burned-cell count (mean of 3 seeds,
jitter 0.2, p0 = 0.12), both rules, both wind laws, four winds.**
`*` marks a checkpoint with boundary contact (west/upwind edge unless
noted) — read those cells as unreliable, not as data.

| wind | rule | law | 2,000 | 5,000 | 10,000 | 20,000 |
|---|---|---|---|---|---|---|
| 0 m/s | bernoulli | exponential | 1.112 | 1.061* | 1.189* | 1.375* |
| 0 m/s | bernoulli | rear_focus | 1.112 | 1.061* | 1.189* | 1.375* |
| 0 m/s | arrival | exponential | 1.015 | 1.025* | 1.205* | 1.396* |
| 0 m/s | arrival | rear_focus | 1.015 | 1.025* | 1.205* | 1.396* |
| 2 m/s | bernoulli | exponential | 1.175 | 1.112* | 1.103* | 1.215* |
| 2 m/s | arrival | exponential | 1.031 | 1.025 | 1.020* | 1.123* |
| 2 m/s | arrival | rear_focus | 1.817 | 1.811 | 1.814 | 1.818 |
| 5 m/s | bernoulli | exponential | 1.088 | 1.128 | 1.123* | 1.155* |
| 5 m/s | arrival | exponential | 1.141 | 1.140 | 1.136 | 1.137* |
| 5 m/s | arrival | rear_focus | 5.638 | 5.728 | 5.763 | 5.757 |
| 8 m/s | bernoulli | exponential | 1.222 | 1.203 | 1.165 | 1.114 |
| 8 m/s | arrival | exponential | 1.310 | 1.309 | 1.308 | 1.308 |
| 8 m/s | arrival | rear_focus | 22.054 | 22.742 | 23.032 | 23.094 |

Rows missing from this table (Bernoulli + rear-focus at wind ≥ 2 m/s,
most seeds) died before the first checkpoint — see finding 4. At wind =
0, `rear_focus` and `exponential` give identical numbers, exactly as
expected (`LB(0) = 1` collapses the two laws to the same formula) — a
free consistency check that the implementation has no wind-law-specific
bug. p0 = 0.22 (not tabulated) reproduces every arrival/rear-focus and
arrival/exponential number in this table within 0.01 — the length-
to-breadth ratio genuinely does not depend on p0, confirming the design
note in `cella_lib/examples/wildfire_ros.rs`.

**Result — Table 3: length-to-breadth vs. Anderson's `LB(U)`, arrival
rule, p0 = 0.12, both jitter settings, at the two largest (least
boundary-affected) checkpoints.** `*` = boundary contact.

| wind | law | c2 | cells | LB (jitter 0.2) | /Anderson | LB (jitter 0) | /Anderson | Anderson |
|---|---|---|---|---|---|---|---|---|
| 0.9690 m/s | rear_focus (fix round 3 template LB 1.2) | — | 10,000 | n/a (jitter 0 only) | — | 1.398 | **1.16** | 1.200 |
| 2 m/s | exponential | 0.131 | 20,000 | 1.123* | 0.75 | 1.106 | 0.73 | 1.505 |
| 2 m/s | exponential | 0.45 | 10,000 | 1.242 | 0.83 | 1.320 | 0.88 | 1.505 |
| 2 m/s | rear_focus | — | 20,000 | 1.818 | **1.21** | 2.009 | 1.33 | 1.505 |
| 3.1771 m/s | rear_focus (fix round 3 template LB 2.0) | — | 10,000 | n/a (jitter 0 only) | — | 3.182 | **1.59** | 2.000 |
| 5 m/s | exponential | 0.131 | 10,000 | 1.136 | 0.36 | 1.193 | 0.37 | 3.192 |
| 5 m/s | exponential | 0.45 | 10,000 | 1.849 | 0.58 | 2.077 | 0.65 | 3.192 |
| 5 m/s | rear_focus | — | 10,000 | 5.763 | **1.81** | 7.327 | 2.30 | 3.192 |
| 8 m/s | exponential | 0.131 | 10,000 | 1.308 | 0.19 | 1.399 | 0.20 | 7.028 |
| 8 m/s | exponential | 0.45 | 10,000 | 2.622 | 0.37 | 3.105 | 0.44 | 7.028 |
| 8 m/s | rear_focus | — | 10,000 | 23.032 | **3.28** | 33.253 | 4.73 | 7.028 |

The two fix-round-3 rows (template LB 1.2 and 2.0) were run at jitter 0
only, by design (bracketing points to nail down the exact shape of the
overshoot-vs-LB curve, not another 3-seed mean) — see the fix-round-3
addendum below for the full 5-point jitter-0 series and the boundary
flag at LB 1.2's 20,000-cell checkpoint.

(Full table — all four `c2` values, both checkpoints, both p0 — is in
`exp30a_arrival_flat.json`; the pattern is monotonic in `c2` and flat
across the two checkpoints everywhere shown here.) The jitter-0 rear-
focus/8 m/s/20,000-cell cell is boundary-contaminated (front reaches
x = 899) and is dropped in favour of the 10,000-cell reading above,
which is clean.

**Closed-form check (jitter 0, isolates the direction law from
per-cell noise): exponential, c2 = 0.131, 8 m/s: measured 1.399 vs.
`cosh(c2·v) = 1.601` — 13 % short, within the pre-registered 15 %
bound.** Rear-focus, 5 m/s: measured 7.327 vs. Anderson 3.192 — 130 %
*over*, not the 21 % *under* fix round 1 found; see finding 3 for why.
Both numbers are also unit tests
(`arrival_rule_closed_form_length_to_breadth_matches_cosh_under_exponential`,
`...matches_anderson_under_rear_focus`), re-run on this same domain.

**Prediction check** (the v2 prediction from TEST_PLAN v1.8: elongation
flat with size at every wind for both laws; jitter-0 LB equals
`cosh(c2·v)` within 15 % under the exponential law and is within 20 %
of Anderson under rear-focus at 2/5/8 m/s; p0 0.12 fires no longer die):

1. *Elongation flat with size, both laws.* **Confirmed** for arrival
   under both laws, at every wind where boundary contact does not
   intrude (Table 2's un-starred arrival/rear-focus row is flat to
   three decimals at every wind; arrival/exponential is flat wherever
   clean). Fix round 1's "collapse" is retracted — it was the
   boundary, not the rule.
2. *Jitter-0 exponential LB within 15 % of `cosh(c2·v)`, c2 = 0.131,
   8 m/s.* **Confirmed** — 13 % short.
3. *Jitter-0 rear-focus LB within 20 % of Anderson at 2/5/8 m/s, every
   valid checkpoint.* **Refuted at all three winds, and by a growing
   margin**: 33 % over at 2 m/s, 130 % over at 5 m/s, 373 % over at
   8 m/s (jitter-0 column) — worse, not better, once the boundary
   artefact is gone, and the miss is now an *overshoot* rather than
   fix round 1's undershoot. See finding 3.
4. *p0 0.12 fires no longer die.* **Confirmed for arrival** (every
   arrival row in Table 2 and Table 3 completes; the far-edge unit
   test passes with default jitter). **Not tested for Bernoulli** —
   the prediction was about arrival specifically, but Table 2 shows
   Bernoulli dying under rear-focus at wind ≥ 2 m/s regardless (see
   finding 4), for a reason unrelated to this fix (its own finite
   `burn_duration` ignition window, unchanged by the domain).

**What it means.**

1. **The rule itself is exactly as designed: self-similar, and its
   rate matches the closed form to within 1 % wherever the boundary
   isn't involved.** Fix round 1's headline finding (rear-focus
   collapses with size) is withdrawn; it was measuring a 400×400 grid's
   edge, not the kernel.
2. **Neither wind law's magnitude matches Anderson, and they miss in
   opposite directions.** The exponential law was already known
   (fix round 1) to fall further below Anderson as wind rises even at
   its highest tested `c2` (0.45): 0.83× at 2 m/s down to 0.37× at
   8 m/s. Rear-focus does the mirror image: 1.21× at 2 m/s up to 3.28×
   at 8 m/s (jitter 0.2; jitter-0 is worse still). If a law existed
   partway between these two constructions, it might land on Anderson's
   curve — an open question for Task 8, not resolved here.
3. **Rear-focus's overshoot has a specific, understood geometric cause:
   the 8-direction grid can't sample a highly eccentric ellipse finely
   enough, so the shape that actually propagates is a "needle" polygon,
   thinner than the ellipse it's built from — and that needle does not
   fatten back out as the fire grows.** Worked through in detail, with
   numbers, in the fix-round-3 addendum immediately below (this was
   documented, not fixed, this round — no rule change).
4. **Bernoulli's fragility under rear-focus is unrelated to the domain
   fix and persists.** It died on 0/3 to at best a handful of seeds at
   wind ≥ 2 m/s in Table 2, same mechanism fix round 1 found (its
   ignition window is bounded by `burn_duration`, and rear-focus's very
   low crosswind/back probability can exhaust it). At p0 = 0.22 it
   survives a little further (e.g. wind = 2 completes; wind = 5 reaches
   5,000 cells before dying) — higher p0 buys it some margin, but does
   not remove the structural difference from arrival, which never has
   this failure mode at any p0 tested.
5. **The real fires' own wind speeds (E41: 0.5–0.7 m/s) are far below
   every wind tested here (2/5/8 m/s), and the overshoot shrinks sharply
   at lower wind** (2 m/s's 21–33 % over is already much smaller than
   8 m/s's 230–373 %, and `(a+c)²` at 0.6 m/s is 2.6, far below 2 m/s's
   6.9). This is not measured directly in this task, but it is the
   most likely reason rear-focus's magnitude problem may matter less
   for the six real fires than these numbers suggest — flagged as an
   open, not a resolved, point.

### Fix round 3 addendum — why rear-focus overshoots, in numbers, and the regime it's actually validated for

Fix round 2 established *that* rear-focus overshoots Anderson once the
domain-boundary artefact is gone, growing from 21% over at 2 m/s to
230%+ over at 8 m/s (jitter 0.2 column of Table 3). This addendum
explains *why*, with the mechanism traced back to the actual formula
in `factors_for_vector`, and states plainly which wind range the
(arrival, rear_focus) recommendation is actually good for. No code
changed this round — this is documentation plus two extra data points.

**The mechanism: an 8-direction grid can't sample a needle-thin ellipse.**
`rear_focus`'s `r(θ) = 1/(a − c·cosθ)` (`a = LB(v)`, `c = √(a²−1)`) is
the exact polar equation of Anderson's ellipse measured from its own
rear focus — a smooth curve, correct at every angle. But the arrival
rule only ever evaluates it at the 8 grid directions (0°, 45°, 90°, …),
and the ellipse gets *more* front-loaded onto the single θ=0 direction
as `a` grows, because `r(θ)` falls off faster near θ=0 the more
eccentric the ellipse is. Concretely, at `a = 7` (≈ LB at 8 m/s):

- head (`θ=0`, the far vertex from the focus): `r(0) = a + c = 13.93`
- 45° neighbour (a diagonal grid step): `r(45°) = 1/(7 − 6.93·cos45°) ≈ 0.48`
- flank (`θ=90°`): `r(90°) = 1/a ≈ 0.14`

The diagonal direction — the very next sample the 8-neighbour grid has
after the head — is already down to 0.48, a 29× drop from the head's
13.93, and only ~3.4× above the flank's 0.14. A smooth ellipse doesn't
fall off nearly that fast between 0° and 45°; the grid's coarse angular
sampling turns the ellipse into a "needle": one long spike along the
exact downwind direction, with everything else collapsed close to the
flank value.

That needle is thinner than the ellipse it's approximating, and the gap
is directly visible in the polygon geometry. Take `a = 2` (LB=2, this
round's own second template point) and walk the straight edge the
8-direction polygon draws between its head vertex (at focus-frame
`x = a+c = 3.73, y = 0`) and its 45°-neighbour vertex (at
`x = r(45°)·cos45° ≈ 0.91, y ≈ 0.91`, `r(45°) = 1/(2−1.73·cos45°) ≈ 1.29`).
At `x = 2.5` along that edge, linear interpolation puts the polygon's
half-width at **0.39**. The true ellipse at that same `x` (semi-major
`a=2`, semi-minor `b=1`, measured from the same focus) has half-width
**0.93** — the polygon is **less than half as wide** as the ellipse it
was built from, at a point well inside the shape, not just out at the
tips. A shape that's this much narrower for the same length is, by
construction, *more* elongated under the second-moment metric
`elongation()` uses — which is exactly the direction every measured
overshoot in Table 3 goes.

**Why the overshoot doesn't shrink as the fire grows (Table 2's own
"flat with size" finding, read the other way).** Minimum-travel-time
propagation on a fixed 8-direction lattice is a shortest-path metric,
and a shortest-path metric's reachable-set-in-time-`t` is the
`t`-scaled copy of its own unit ball (the local per-tick reach
polygon), because combining shortest paths through many hops is a
repeated Minkowski sum of that same polygon with itself — and a convex
polygon's Minkowski self-sum is just a bigger copy of the same polygon,
never a rounder one. The needle traced out at 1 tick is (up to
lattice-alignment noise near the origin) the same needle at 20,000
ticks, just larger. That is *why* Table 2's rear-focus rows are flat
across checkpoints (1.814/1.811/1.814/1.818 at 2 m/s) at the *needle's*
elongation, not the ellipse's — "self-similar" and "matches Anderson"
turned out to be two different claims, and only the first one is true
here.

**The error grows with `a` because the needle gets sharper, not because
of noise.** The jitter-0.2 `/Anderson` ratios already in Table 3 are the
data for this: **1.21× over at LB≈1.5** (2 m/s), **1.81× over at
LB≈3.2** (5 m/s, rounds to "1.8×"), **3.28× over at LB≈7** (8 m/s,
rounds to "3.3×"). Higher `a` pushes more of `r(θ)`'s area under the
single head sample and starves the 45° neighbour faster (its value fell
from 0.48/13.93 = 3.4% of head at `a=7` down to a much larger fraction
of head at low `a`), so the needle-vs-ellipse gap — and the overshoot —
widens monotonically with wind. This is a property of the *sampling*,
not of any noise or seed.

**Two new template points, jitter 0, confirming the trend holds off the
three original winds too** (same 900×300 upwind-ignition domain,
p0 = 0.12, checkpoints 10,000/20,000 cells; wind chosen by bisecting
`anderson_lb(v)` to hit the target exactly rather than using a rounded
guess):

| target LB | wind (m/s) solved for it | measured LB, 10,000 cells | measured LB, 20,000 cells | boundary contact |
|---|---|---|---|---|
| 1.2 | 0.9690 | 1.398 (16% over) | 1.377 (15% over) | no at 10k, **yes at 20k** |
| 2.0 | 3.1771 | 3.182 (59% over) | 3.185 (59% over) | no |

The 10,000-cell reading is the clean one to trust at LB 1.2 (the
20,000-cell checkpoint touches the upwind edge, same the calm/mild-wind
pattern already noted for Table 2 — it doesn't affect the answer here,
since both checkpoints agree to 3%). Both new points slot into the same
jitter-0, 10,000-cell series as the three original winds, and the
result is a clean, monotonic curve — overshoot grows smoothly with
template LB, not by jumps or noise:

| template LB | 1.2 | 1.5 | 2.0 | 3.2 | 7.0 |
|---|---|---|---|---|---|
| wind (m/s) | 0.9690 | 2 | 3.1771 | 5 | 8 |
| measured LB (jitter 0, 10,000 cells) | 1.398 | 2.008 | 3.182 | 7.327 | 33.253 |
| Anderson LB(U) | 1.200 | 1.505 | 2.000 | 3.192 | 7.028 |
| overshoot | +16% | +33% | +59% | +130% | +373% |

This closes the bracket the controller asked for: even at the low end
(template LB 1.2, barely above the real fires' own Anderson LB of
1.09–1.14 at ERA5 wind speeds), the needle mechanism above is already
producing a measurable 16% overshoot — small enough that the
(arrival, rear_focus) recommendation's *magnitude* claim is usable
there, but not zero, and it only gets worse from here.

**Two real fixes, for the record — neither implemented this round.**
(1) A finer angular neighbourhood (16 or 32 directions instead of 8)
would let the needle track the ellipse's curvature much more closely,
at the cost of a more expensive relaxation per tick. (2) A fitted
correction curve — feed `rear_focus` a smaller "template" `a` than the
Anderson value actually wanted, chosen so that the *needle's* measured
elongation lands on the target — would fix the magnitude without
touching the per-tick cost, at the cost of needing its own calibration
table (and re-deriving it if the grid's direction set or the arrival
rule itself ever changes). Both are real, buildable fixes; this fix
round's brief was to explain the mechanism and add data, not to
implement either one.

**The regime the (arrival, rear_focus) recommendation is actually
validated for: LB ≤ 1.5, not the 2–8 m/s wind range tested here at
face value.** E41's own ERA5 wind speeds for the six real fires are
0.5–0.7 m/s — at those speeds `anderson_lb(v)` is only **1.09–1.14**,
comfortably inside the LB≤1.5 regime where the measured overshoot is
smallest (21% at LB≈1.5, and presumably less at LB≈1.1–1.2, per the new
template-1.2 point above). The overshoot only becomes severe (81% at
LB≈3.2, 228%+ at LB≈7) at wind speeds well above anything E41 found in
the real data this model targets. What still matters at those real
wind speeds is the *sign* asymmetry E41 actually needs — head:back
`(a+c)² = 2.59` at 0.6 m/s, confirmed above — not the magnitude, which
this addendum shows is only trustworthy up to LB≈1.5.

**The ensemble's actual ceiling is lower still: LB ≤ ~1.3, not just
≤ 1.5.** The wildfire ensemble has its own free `wind_scale` gene
(`cella_lib/src/explore/genome.rs`, consumed by
`wildfire/driver.rs::GENE_WIND_SCALE`) that multiplies the ERA5-supplied
wind by a factor evolution can pick anywhere in `[0.0, 1.5]` — it can
only ever turn the wind the model actually runs at *down* from ERA5, or
up to 1.5× it, never higher. Combined with the fires' own 0.5–0.7 m/s
ERA5 range, the highest wind speed the kernel will ever actually see
during evolution is `0.7 × 1.5 = 1.05` m/s (`anderson_lb(1.05) ≈ 1.22`;
the low end, `0.5 × 1.5 = 0.75` m/s, gives `anderson_lb(0.75) ≈ 1.15`).
So in practice this kernel runs at **LB ≤ ~1.3**, not merely ≤ 1.5, and
the overshoot it actually experiences is **≤ ~20%** (interpolating the
template table above: +16% at LB 1.2, +33% at LB 1.5 — LB 1.22–1.3 lands
between those, near +18–20%). The LB ≤ 1.5 validated regime below
therefore carries real margin over the ensemble's true operating point;
LB ≤ 1.5 is the recommendation's stated boundary because it is where the
20,000-cell template-1.5 point was actually measured, not because the
ensemble needs to go that high.

**Recommendation for E30 (Task 8), from the corrected measurement.**
Still **arrival** — every property checked here (self-similar shape,
rate matches the closed form, no death threshold) holds up under
correct measurement. For the wind law: **rear_focus**, for the same
reason fix round 1 gave (it is the only option that reproduces E41's
front/back *sign*, which is the actual finding driving this whole
redesign) — but **the combination is validated only for LB ≤ 1.5**
(wind ≲ 2 m/s in this model's own units), which safely covers the six
real fires' own ERA5 wind speeds (LB 1.09–1.14). Do not extrapolate the
magnitude claim to higher wind: the overshoot documented above is a
real, understood geometric property of the 8-direction grid, grows
sharply with `a`, and has no tunable parameter (unlike the exponential
law's `c2`) to correct it within this fix round's scope. If Task 8 ever
needs to run this model at wind speeds materially above 0.7 m/s, revisit
one of the two real fixes above first. No `c2` is recommended under the
exponential law for the same reason as fix round 1: every value
undershoots, worse at higher wind.

**Discipline: the Bernoulli path is unchanged (this fix round too).**
Only `illuminate`/`lb`'s domain, checkpoint scheme, and the two closed-
form unit tests changed this round — `step_chunk_bernoulli` and
`step_chunk_arrival` are untouched. The wildfire snapshot/hash stress
tests (`cella_lib/tests/long_suite.rs`) pass unmodified at 1/4/8
threads, with and without spotting.

**Questions this raises.**

- Is there a direction-law construction that lands on Anderson's `LB(U)`
  as a *second-moment* elongation rather than as a geometric
  eccentricity parameter — i.e., one that accounts for the shape's own
  asymmetry rather than assuming it away? Open; this is now the central
  question for Task 8/E30's own wind-law choice.
- What is rear-focus's actual overshoot at the real fires' own wind
  speeds (0.5–0.7 m/s), not the 2/5/8 m/s tested here? Open.
- Does a higher p0 (beyond 0.22) let Bernoulli survive rear-focus at
  higher wind, or is 0.44 (the pre-registered trio's top) still not
  enough? Open, and now of secondary interest since arrival is the
  recommended rule regardless.

**Verdict.** Finding — the fix-round-1 "collapses with size" result is
retracted (domain artefact); the rule's self-similarity and rate
semantics are both confirmed cleanly; neither wind law's magnitude
matches Anderson, in opposite directions, and rear-focus's is explained
by a real, understood mechanism (asymmetric-shape second-moment
inflation) rather than left as an unexplained miss.

**Later.** E30 (Task 8, not yet run).

---

## v2, fix round 1 — centred 400×400, boundary-limited (superseded)

_Everything in this section was measured on a centred 400×400 grid at
percentage-of-grid checkpoints, later found (fix round 2, above) to let
the fire's own head reach the domain boundary at almost exactly the
10 % checkpoint under rear-focus at high wind. The "elongation collapses
with size" finding below is a domain artefact, not a property of the
arrival rule; see the fix-round-2 section above for the corrected
measurement and the current recommendation. Also, every `cosh(c2·v)`
figure below (this round's own exponential-law closed form) is wrong —
a controller error found in fix round 5; see the v2b section at the
top of the file for the corrected closed form and why the 90°-flank
assumption fails. Kept verbatim for the record._

**In short.** v1 of the arrival rule (a per-cell "heat" accumulator) had
two flaws its own tables exposed: heat only came from currently-burning
neighbours, so a cell whose neighbours all burned out before its heat
reached 1 never ignited (forcing p0 = 0.44 and, for the length-to-breadth
table, burn_duration = 500 as workarounds); and heat summed contributions
in a way that flattened direction ratios (v1's own closed-form check
measured 1.09 against a required 1.60). A controller fix round redefined
`spread: "arrival"` as **minimum travel time**, the standard fire-CA
formulation: every fuel cell keeps an *arrival time* in ticks, and a
still-unburned cell relaxes its own arrival time to the smallest
`neighbour's arrival + cost-to-cross-that-edge` over every
burning-or-already-burned neighbour, every tick. This has no death
threshold at all — a burnt-out cell is still a source forever — so the
pre-registered p0 = 0.12 now runs cleanly. The exponential wind law's own
closed-form check (jitter off, so it is a pure test of the direction
law) now lands within 13% of the required value, and arrival's
elongation-vs-size curve stays close to flat under that law at every
wind. But the picture is not uniformly good news: the rear-focus law,
even under minimum travel time, still shows a clear elongation-collapses-
with-size pattern at wind ≥ 2 m/s — the opposite of what this task's own
pre-registered prediction expected — and its length-to-breadth ratio
under-shoots Anderson (1983) by a widening margin as wind rises. Both are
reported as measured, not smoothed over.

**Question.** Does making wind set ignition *time* instead of ignition
*chance* give a kernel whose elongation does not collapse with size? And
what `c2` (or rate law) makes its length-to-breadth match Anderson 1983?

**What changed from v1.** `cella_lib/src/wildfire/mod.rs`:
`WildfireDerived::heat` → `WildfireDerived::arrival` (ticks; `0` for a
cell that starts already burning or burned, `+inf` otherwise). Each
tick, a still-unburned fuel cell with a burning-or-burned neighbour `j`
computes `arrival[cell] = min(arrival[cell], min_j(arrival[j] +
cost_j))`, `cost_j = jitter(cell) · norm_j / (p_base[cell] · dir[j] ·
slope[cell, j])` (`norm_j` = 1 cardinal, `√2` diagonal — a genuine
distance-over-speed calculation, not the same use of `1/norm` that is
already baked into `dir[j]` for the Bernoulli probability), clamped to
`>= 1` tick, and the cell ignites the first tick its own tick number
reaches that value. A neighbour only counts as a source once it shows up
as burning-or-burned in the *previous* tick's snapshot, so a same-tick
ignition can never be (mis)used as a source — checked directly by a unit
test. `burn_duration` no longer has any influence on *when* a cell
catches (only on how long it stays visibly burning, and so spot-
eligible). Nothing about `wind_law`, `arrival_jitter`, or the Bernoulli
rule changed in this fix round.

**How we measured it.** Same three synthetic-grid measurements as v1,
re-run with the pre-registered settings (no more artificial p0 = 0.44 or
burn_duration = 500 — the death threshold that required them is gone):

1. **Flat-grid front speed** (`arrival_flat` mode; 240 × 120 uniform
   fuel, a full-height burning column at x = 0..2): both rules, wind
   0/2/5/8 m/s, p0 0.12/0.22/0.44, burn duration 5/10, 3 seeds.
2. **Point-ignition elongation vs. size** (`illuminate` mode): a 3×3
   ignition at the centre of a 400×400 uniform grid, wind toward +x at
   0/2/5/8 m/s, elongation at 2/5/10/20 % burned, both rules, 3 seeds,
   p0 = 0.12, burn duration = 5 (the pre-registered trio's low end — no
   longer ruled out, since arrival cannot die). A second pass swaps in
   the rear-focus law (arrival rule only) to check the addendum's other
   clause.
3. **Length-to-breadth at 10 % size** (`lb` mode, arrival rule only):
   wind 2/5/8 m/s, `c2` ∈ {0.131, 0.2, 0.3, 0.45} under the exponential
   law plus once under rear-focus, against Anderson's `LB(U)`, p0 =
   0.12, burn duration = 5 — chosen for consistency with (2); neither
   parameter enters the arrival-time relaxation's direction *ratios* at
   all (`p_base` is common to every direction and cancels; burn duration
   never appears in the formula), so one representative value stands in
   for the full pre-registered (p0, duration) grid. Each (law, c2, wind)
   is reported twice: the default `arrival_jitter = 0.2` (3 seeds, mean)
   and a single deterministic `arrival_jitter = 0` reading, so the
   closed-form checks below can be read straight off the table.

**Result — Table 1: flat-grid front speed (cells/tick), both rules,
burn duration 5** (duration 10 is identical to 3 decimals for *both*
rules under v2 — see finding 4):

| p0 | Bern 0 | Bern 2 | Bern 5 | Bern 8 | Arr 0 | Arr 2 | Arr 5 | Arr 8 |
|---|---|---|---|---|---|---|---|---|
| 0.12 | 0.474 | 0.477 | 0.496 | 0.515 | 0.123 | 0.134 | 0.153 | 0.174 |
| 0.22 | 0.701 | 0.709 | 0.728 | 0.755 | 0.227 | 0.246 | 0.280 | 0.319 |
| 0.44 | 0.960 | 0.968 | 0.983 | 0.994 | 0.453 | 0.493 | 0.560 | 0.637 |

Arrival's own speed is now exactly proportional to p0 (0.227/0.123 =
1.85 ≈ 0.22/0.12 = 1.83; 0.453/0.123 = 3.68 ≈ 0.44/0.12 = 3.67) — a
direct, explainable consequence of `cost = jitter·norm / (p_base ·
dir · slope)` being linear in `p_base`, unlike v1's saturating,
non-proportional heat accumulation.

**Result — Table 2a: elongation vs. size (mean of 3 seeds), exponential
law, both rules, four winds.** See also the figure.

| wind | rule | 2 % | 5 % | 10 % | 20 % | range |
|---|---|---|---|---|---|---|
| 0 m/s | bernoulli | 1.157 | 1.101 | 1.061 | 1.050 | 0.107 |
| 0 m/s | arrival | 1.016 | 1.013 | 1.008 | 1.008 | 0.009 |
| 2 m/s | bernoulli | 1.105 | 1.098 | 1.092 | 1.079 | 0.026 |
| 2 m/s | arrival | 1.036 | 1.035 | 1.032 | 1.032 | 0.005 |
| 5 m/s | bernoulli | 1.214 | 1.114 | 1.147 | 1.200 | 0.100 |
| 5 m/s | arrival | 1.160 | 1.159 | 1.156 | 1.153 | 0.007 |
| 8 m/s | bernoulli | 1.080 | 1.050 | 1.048 | 1.520 | 0.473 |
| 8 m/s | arrival | 1.340 | 1.328 | 1.330 | 1.277 | 0.063 |

**Result — Table 2b: elongation vs. size (mean of 3 seeds), rear-focus
law, arrival rule only** (Bernoulli under rear-focus died before 2 %
burned on all 3 seeds at wind ≥ 2 m/s — see finding 5):

| wind | 2 % | 5 % | 10 % | 20 % | range |
|---|---|---|---|---|---|
| 0 m/s | 1.017 | 1.013 | 1.008 | 1.008 | 0.009 |
| 2 m/s | 1.849 | 1.846 | 1.845 | 1.493 | 0.356 |
| 5 m/s | 5.700 | 4.581 | 2.543 | 1.358 | 4.342 |
| 8 m/s | 10.000 | 4.903 | 2.472 | — (not reached by 20,000 ticks) | — |

**Result — Table 3: length-to-breadth at 10 % size, arrival rule, both
laws, against Anderson's `LB(U)`, default jitter (0.2, mean of 3 seeds)
and jitter 0 (deterministic).**

| wind | law | c2 | LB (jitter 0.2) | LB (jitter 0) | Anderson LB(U) | LB(0.2)/Anderson |
|---|---|---|---|---|---|---|
| 2 m/s | exponential | 0.131 | 1.032 | 1.036 | 1.505 | 0.69 |
| 2 m/s | exponential | 0.2 | 1.064 | 1.080 | 1.505 | 0.71 |
| 2 m/s | exponential | 0.3 | 1.134 | 1.165 | 1.505 | 0.75 |
| 2 m/s | exponential | 0.45 | 1.263 | 1.319 | 1.505 | 0.84 |
| 2 m/s | rear_focus | — | 1.845 | 2.000 | 1.505 | **1.23** |
| 5 m/s | exponential | 0.131 | 1.156 | 1.192 | 3.192 | 0.36 |
| 5 m/s | exponential | 0.2 | 1.308 | 1.372 | 3.192 | 0.41 |
| 5 m/s | exponential | 0.3 | 1.538 | 1.643 | 3.192 | 0.48 |
| 5 m/s | exponential | 0.45 | 1.864 | 1.901 | 3.192 | 0.58 |
| 5 m/s | rear_focus | — | 2.543 | 2.567 | 3.192 | 0.80 |
| 8 m/s | exponential | 0.131 | 1.330 | 1.398 | 7.028 | 0.19 |
| 8 m/s | exponential | 0.2 | 1.584 | 1.697 | 7.028 | 0.23 |
| 8 m/s | exponential | 0.3 | 1.905 | 1.924 | 7.028 | 0.27 |
| 8 m/s | exponential | 0.45 | 2.041 | 2.012 | 7.028 | 0.29 |
| 8 m/s | rear_focus | — | 2.471 | n/a (20,000-tick budget exhausted) | 7.028 | 0.35 |

Closed-form check at `c2 = 0.131`, `v = 8` (jitter 0, isolating the
direction law): `(head + back) / (2·flank) = cosh(c2·v)` should be
`cosh(1.048) = 1.601`; measured **1.398** (13 % short — within the unit
test's 15 % bound). Closed-form head:back ratio at 0.6 m/s (no
simulation, `dir[head] / dir[back]` from the wind law directly):
**exponential (default c2 = 0.131) = 1.17; rear_focus = 2.59**.

**Prediction check, line by line** (v2's own prediction, TEST_PLAN
v1.8): *"elongation flat with size at every wind for both laws;
jitter-0 LB equals cosh(c2·v) within 15 % under the exponential law and
is within 20 % of Anderson under rear-focus at 2/5/8 m/s; p0 0.12 fires
no longer die."*

1. *"elongation flat with size at every wind for both laws."*
   **Confirmed for the exponential law** (Table 2a: arrival's own range
   is ≤ 0.063 at every wind). **Refuted for rear-focus** (Table 2b): flat
   only at calm (no anisotropy to begin with); at 2 m/s it holds through
   10 % then drops 19 % by 20 %; at 5 m/s it falls monotonically and
   dramatically (5.70 → 1.36, a factor of 4.2); at 8 m/s it clamps at
   the metric's own maximum (10.0) at 2 % and falls to 2.47 by 10 %,
   never reaching 20 % inside the step budget. See finding 2.
2. *"jitter-0 LB equals cosh(c2·v) within 15 % under the exponential
   law."* **Confirmed at the one point the unit test checks** (c2 =
   0.131, 8 m/s: 1.398 vs 1.601, 13 % short). **Not confirmed in
   general** — Table 3's own jitter-0 column shows the gap widening
   sharply as `c2·v` grows (e.g. c2 = 0.45, v = 8: cosh = 18.3, measured
   2.01, 89 % short). See finding 3.
3. *"[jitter-0 LB] within 20 % of Anderson under rear-focus at 2/5/8
   m/s."* **Confirmed only at 5 m/s** (0.80, within bound). **Refuted at
   2 m/s** (measured *exceeds* Anderson by 33 %: jitter-0 LB 2.00 vs
   1.505) **and at 8 m/s** (n/a — did not reach 10 % burned inside the
   20,000-tick budget; the default-jitter mean it did reach, 2.47, is
   35 % of Anderson, 65 % short).
4. *"p0 0.12 fires no longer die."* **Confirmed.** Every row of Table 3
   reports 3/3 seeds reaching 10 % burned (the raw `exp30a_arrival_flat.json`
   still carries the `reached_seeds`/`total_seeds` fields from the v1
   plumbing, now always 3/3); the 60×60-grid unit test
   (`arrival_rule_reaches_the_far_edge_without_dying`) confirms this
   directly at the *default* `arrival_jitter` (0.2, not silenced),
   p0 = 0.12, burn_duration = 5 — the exact combination that died in v1.

**What it means.**

1. **The death threshold is genuinely gone**, and with it goes the
   entire class of workaround parameters (p0 = 0.44, burn_duration =
   500) v1 needed. This was the more basic of the two v1 flaws and the
   fix is unambiguous.
2. **Minimum travel time does not, by itself, guarantee a size-
   independent shape — that depends on the direction law.** Under the
   mild, smoothly-varying exponential law, arrival's elongation is
   close to flat (matching the original E30a brief's own claim). Under
   the sharply peaked rear-focus law, arrival still shows a strong
   elongation-collapses-with-size pattern, structurally the *same
   qualitative failure* E37 first found in Bernoulli, just for a
   different mechanical reason: a rear-focus point ignition starts as
   an almost one-dimensional spine (only the exact downwind cardinal
   direction is fast), which reads as extremely elongated at 2 % burned
   (up to the metric's own clamp of 10.0 at 8 m/s), and only gradually
   thickens toward a more elliptical shape as slower directions
   accumulate enough ticks to catch up. "Minimum travel time" fixes
   *Bernoulli's* saturation mechanism, but a strongly anisotropic
   direction law can still produce a shape whose *transient* is far
   more stretched than its (much rounder) longer-run character — a
   genuinely different, and unsolved, way to get "shape depends on
   size."
3. **The closed-form check is a good approximation only for mild
   anisotropy.** It was derived by treating the fire's reach in three
   cardinal directions (head, back, flank) as directly proportional to
   each direction's speed and combining them into one ratio — a fair
   approximation when the whole shape is close to an ellipse, which is
   true for small `c2·v`, but increasingly wrong as `c2·v` grows and the
   true second-moment shape (what `elongation()` actually measures)
   diverges from a clean ellipse. This is why the unit test's single
   checked point (c2 = 0.131, v = 8, product 1.05) passes comfortably
   while c2 = 0.45 at the same wind (product 3.6, "predicted" cosh =
   18.3) misses by an order of magnitude — not a discretization bug, a
   property of the closed form's own derivation.
4. **Front speed is duration-independent for arrival, exactly as
   designed** (Table 1's duration-10 column is identical to duration 5
   to 3 decimals, for *both* rules under these settings — Bernoulli's
   own duration-independence here is coincidental to this speed
   regime, not a general property the way it is for arrival by
   construction).
5. **Bernoulli can now fail outright, not just saturate, under a
   strongly directional law.** Under rear-focus at wind ≥ 2 m/s,
   Bernoulli's point ignition died before 2 % burned on 3/3 seeds at
   every wind tested (0 rows recorded past wind = 0 in Table 2b): its
   ignition window is bounded by `burn_duration` (5 ticks here), and
   rear-focus's crosswind/back probabilities are low enough that the
   whole 3×3 patch can burn out before successfully igniting any
   neighbour. Arrival never has this failure mode — a burnt-out cell
   remains a source forever, so a slow direction just takes longer,
   never "never." This is a genuine, additional point in arrival's
   favour beyond the shape claim, not one either prediction named.

**Recommendation for E30 (Task 8), from v2.** Use the **arrival rule**
— it is strictly better than Bernoulli in every measurement here (never
dies, exact size-independence under the exponential law, duration-
independent speed) — but **do not pair it with `rear_focus` and expect
size-independent shape**: that combination reproduces E37's original
failure mode (elongated-only-while-small) for a new reason. If E30's
priority is the front/back *sign* E41 found (rear-focus's head:back ≥ 2
already at 0.6 m/s, confirmed here), accept that its shape will still
depend on fire size and calibrate at (or near) the size actually being
compared against; if E30's priority is a stable shape across sizes,
stay with the exponential law but do not expect it to reach Anderson's
magnitude — even c2 = 0.45 (the most extreme value tested) reaches only
29 % of Anderson at 8 m/s. No single `c2` is recommended for matching
Anderson under the exponential law: the shortfall *grows* with wind
(0.84 → 0.58 → 0.29 at c2 = 0.45 across 2/5/8 m/s), so any one value is
only "least wrong" at whichever wind it happens to be tuned to.

**Discipline: the Bernoulli path is unchanged (this fix round too).**
`step_chunk_bernoulli` was not touched in this fix round (only
`step_chunk_arrival` and `WildfireDerived::arrival` changed). The
pre-existing wildfire snapshot/hash stress tests
(`cella_lib/tests/long_suite.rs`, `stress_2d_wildfire_t1/t4/t8`, with
and without spotting) pass unmodified against their stored hashes at 1,
4 and 8 threads after this fix round's changes.

**Questions this raises.**

- What direction law is both size-independent under arrival *and*
  reaches Anderson's magnitude? Neither law tested here is — open, and
  now the central question for Task 8/E30.
- Does rear-focus's transient (very elongated when small, rounder as it
  grows) resemble anything in the six real fires' own early growth, or
  is it purely an artifact of a single point ignition on a uniform
  grid? Open.
- Would a longer step budget (past 20,000 ticks) let rear-focus at
  8 m/s finish thickening toward a stable ratio, and would that ratio
  be closer to or further from Anderson? Open — Table 2b's own 8 m/s
  row did not reach 20 % burned inside the budget used here.

**Verdict.** Finding — arrival's death-threshold and closed-form flaws
from v1 are fixed, but the rear-focus law's own shape is not yet the
size-independent, Anderson-matching kernel E30 needs; the exponential
law is size-independent but does not reach Anderson at all. Neither
prediction clause about rear-focus (size-independence, LB within 20 %
at all three winds) was fully confirmed.

**Later.** E30 (Task 8, not yet run).

---

## v1 — heat accumulator (superseded, kept for the record)

_This section is the original E30a report, unedited except for this
heading. It describes the version of the arrival rule that shipped
first and was found, by the tables below, to have a death threshold and
a saturated head; see the controller fix-round message and the v2
section above for what replaced it. `params.spread = "arrival"` now
means the v2 (minimum-travel-time) rule; nothing here still describes
the code as it exists after this fix round._

**In short.** E37 found the fire model's large fires come out round
because its Bernoulli spread rule rolls one ignition-probability coin
per tick per burning neighbour, which saturates once enough neighbours
are burning — wind changes how *often* a cell catches, not how *long*
it takes. This task added a second rule, **arrival**: the same
per-direction wind/slope number is used as a *rate* accumulated into a
per-cell **heat** counter until it reaches 1, so direction sets ignition
*time* instead. On a flat, uniform grid (no terrain or fuel
heterogeneity, so wind is the only possible source of shape), arrival's
elongation stays flat within ±0.02 as a point-ignition fire grows from
2 % to 20 % of a 400×400 grid, at every wind tested — the prediction
holds. Bernoulli's own elongation, though, never got anywhere near the
predicted 1.5 at 2 % burned in the first place (it peaked at 1.08), so
the predicted collapse from "very elongated" to "round" could not be
observed — there was nothing to collapse from. The length-to-breadth
table shows the default wind law falls well short of Anderson (1983)'s
reference curve at every wind and every `c2` tried; a new **rear-focus**
wind law (added by controller ruling after E41) gets the *sign* right
immediately (head:back ≥ 2 at 0.6 m/s, as E41 needed) but only
approaches Anderson's magnitude at low wind — at high wind it is still
visibly widening within the 10 % burned window the table measures, so
the numbers reported here are a lower bound, not a converged shape.

**Question.** Does making wind set ignition *time* instead of ignition
*chance* give a kernel whose elongation does not collapse with size? And
what `c2` (or rate law) makes its length-to-breadth match Anderson 1983?

**What we changed.** `cella_lib/src/wildfire/mod.rs` gained:

- `params.spread: "bernoulli" | "arrival"` (default `"bernoulli"`, so
  every existing config and every existing test is byte-for-byte
  unchanged — proven below). Under `"arrival"`, each fuel cell with a
  burning neighbour adds `p_base × dir[j] × slope[cell, j] × jitter` to
  its `heat` every tick, for every burning neighbour `j`, and ignites
  once `heat >= 1`. `jitter` is a per-cell log-normal multiplier
  (`params.arrival_jitter`, default 0.2 σ), drawn once per cell for the
  whole run from a dedicated `cell_rand` stream, so runs stay
  bit-reproducible and ensembles (different seeds) still see different
  cells catch at slightly different rates.
- `params.wind_law: "exponential" | "rear_focus"` (default
  `"exponential"`, the existing kernel). `"rear_focus"` is an
  Anderson-1983 ellipse template, `dir[j] = exp(c1·v) · r(θ_j)/r_max`,
  `r(θ) = 1/(a − c·cosθ)`, `a = LB(v)` (this plan's own formula, clamped
  `[1, 8]`), `c = √(a² − 1)`, `r_max = a + c`. At `v = 0`, `a = 1`,
  `c = 0`, so it reduces to the exponential law's own no-wind case
  exactly (checked by unit test).

**Why we expected it to matter.** E37 traced Bernoulli's roundness to a
saturating *probability*: once a cell has several burning neighbours,
each direction's own odds stop mattering because *any* of them is
enough. A *rate*, by contrast, never saturates — a slow direction just
takes longer, so the head:flank *speed* ratio (`dir[head] / dir[flank]`)
should survive at any size and any burn duration. Separately, E41 (the
Ellipse null) found that the shape signal in the six fires' real (ERA5)
wind is a front/back *sign* (rear-focus LB ≈ 1.1, head:back ≈ 2.4), which
the existing exponential kernel's own head:back ratio (`exp(2·c2·v)` =
1.17 at 0.6 m/s) is far too weak to produce.

**How we measured it.** Three synthetic-grid measurements, all wind-only
(no terrain, no fuel classes beyond one, so nothing but the wind kernel
can produce shape):

1. **Flat-grid front speed** (`arrival_flat` mode; E19's own set-up: 240
   × 120 uniform fuel, a full-height burning column at x = 0..2, wind
   toward +x or calm): both spread rules, wind 0/2/5/8 m/s, p0
   0.12/0.22/0.44, burn duration 5/10, 3 seeds.
2. **Point-ignition elongation vs. size** (`illuminate` mode; E12's
   elongation, the second-moment measure): a 3×3 ignition at the centre
   of a 400×400 uniform grid, wind toward +x at 0/2/5/8 m/s, elongation
   recorded when the burned fraction (Burning + BurnedOut) first crosses
   2 %, 5 %, 10 % and 20 %, both rules, 3 seeds. p0 = 0.44, burn duration
   = 5 (see "A calibration note" below for why).
3. **Length-to-breadth at 10 % size** (`lb` mode, arrival rule only): the
   same point ignition, elongation read at the 10 % checkpoint, for wind
   2/5/8 m/s, `c2` ∈ {0.131, 0.2, 0.3, 0.45} under the exponential law,
   plus once under the rear-focus law, against Anderson's own `LB(U)`.
   Also the closed-form head:back ratio at 0.6 m/s for both laws (no
   simulation needed for that number — it is `dir[head] / dir[back]`
   from the wind law directly). p0 = 0.44, burn duration = 500 (see
   below).

**A calibration note, since it affects how to read every table below.**
Two parameters had to be chosen for measurements 2 and 3 that the task
brief left open, and both were derived from the model's own rate
formula *before* looking at any elongation or length-to-breadth result,
not fitted to make either prediction true:

- **p0 = 0.44** (the top of the pre-registered flat-grid trio). A lower
  p0 in the same trio (0.12) let the arrival rule's point ignition die
  out under low wind: a lone downwind neighbour's rate × burn duration
  fell only just above 1, and the default `arrival_jitter` (σ = 0.2) can
  push an individual cell's draw below that margin, permanently
  starving that path (heat stops accumulating once its only supporting
  neighbour has burned out). p0 = 0.44 gives a ≈ 3× margin.
- **Burn duration 500 for the `lb` mode only** (5 everywhere else). At
  `c2 ≥ 0.3` or under `rear_focus`, the crosswind direction factor is so
  small (≈ 0.015 at rear-focus/8 m/s) that at burn duration 5 a single
  upstream neighbour cannot push a crosswind cell's heat anywhere near
  1 before burning out — the fire can only ever advance as a
  one-cell-wide spine, which starves at the grid edge before 10 % of a
  400×400 grid burns (confirmed with the `WF_DEBUG=1` env var: it dies
  at the *same* tiny fraction regardless of grid size, ruling out "just
  needs more room" — a fixed-width spine's *share* of an N×N grid only
  shrinks as N grows). Burn duration 500 gives even the worst case
  (rear-focus, 8 m/s) a margin of ≈ 3 for its crosswind direction to
  self-sustain, so the fire can grow into a measurable 2-D shape at all.
  This also means the `lb` table's numbers are a snapshot of a still
  slowly widening shape, not a converged one — see finding 4.

**Result — Table 1: flat-grid front speed (cells/tick), both rules.**

Duration 5 (duration 10 is the same for both rules to 3 decimals — see
finding 1):

| p0 | Bern 0 | Bern 2 | Bern 5 | Bern 8 | Arr 0 | Arr 2 | Arr 5 | Arr 8 |
|---|---|---|---|---|---|---|---|---|
| 0.12 | 0.474 | 0.477 | 0.496 | 0.515 | 0.256 | 0.265 | 0.280 | 0.297 |
| 0.22 | 0.701 | 0.709 | 0.728 | 0.755 | 0.417 | 0.433 | 0.457 | 0.481 |
| 0.44 | 0.960 | 0.968 | 0.983 | 0.994 | 0.689 | 0.715 | 0.763 | 0.822 |

**Result — Table 2: elongation vs. burned-area size (mean of 3 seeds),
both rules, four winds.**

| wind | rule | 2 % | 5 % | 10 % | 20 % | range |
|---|---|---|---|---|---|---|
| 0 m/s | bernoulli | 1.055 | 1.023 | 1.015 | 1.009 | 0.046 |
| 0 m/s | arrival | 1.030 | 1.021 | 1.016 | 1.012 | 0.018 |
| 2 m/s | bernoulli | 1.061 | 1.048 | 1.035 | 1.027 | 0.034 |
| 2 m/s | arrival | 1.017 | 1.013 | 1.010 | 1.009 | 0.008 |
| 5 m/s | bernoulli | 1.077 | 1.065 | 1.054 | 1.051 | 0.025 |
| 5 m/s | arrival | 1.029 | 1.030 | 1.034 | 1.032 | 0.005 |
| 8 m/s | bernoulli | 1.082 | 1.059 | 1.062 | 1.064 | 0.023 |
| 8 m/s | arrival | 1.073 | 1.081 | 1.088 | 1.090 | 0.017 |

**Result — Table 3: length-to-breadth at 10 % size, arrival rule, against
Anderson's `LB(U)`.**

| wind | law | c2 | LB (mean of 3 seeds) | Anderson LB(U) | LB / Anderson |
|---|---|---|---|---|---|
| 2 m/s | exponential | 0.131 | 1.010 | 1.505 | 0.67 |
| 2 m/s | exponential | 0.2 | 1.028 | 1.505 | 0.68 |
| 2 m/s | exponential | 0.3 | 1.023 | 1.505 | 0.68 |
| 2 m/s | exponential | 0.45 | 1.060 | 1.505 | 0.70 |
| 2 m/s | rear_focus | — | 1.201 | 1.505 | **0.80** |
| 5 m/s | exponential | 0.131 | 1.034 | 3.192 | 0.32 |
| 5 m/s | exponential | 0.2 | 1.081 | 3.192 | 0.34 |
| 5 m/s | exponential | 0.3 | 1.161 | 3.192 | 0.36 |
| 5 m/s | exponential | 0.45 | 1.336 | 3.192 | 0.42 |
| 5 m/s | rear_focus | — | 2.511 | 3.192 | **0.79** |
| 8 m/s | exponential | 0.131 | 1.094 | 7.028 | 0.16 |
| 8 m/s | exponential | 0.2 | 1.223 | 7.028 | 0.17 |
| 8 m/s | exponential | 0.3 | 1.413 | 7.028 | 0.20 |
| 8 m/s | exponential | 0.45 | 1.806 | 7.028 | 0.26 |
| 8 m/s | rear_focus | — | 2.553 | 7.028 | 0.36 |

Closed-form head:back ratio at 0.6 m/s (no simulation, `dir[head] /
dir[back]` from the wind law directly): **exponential (default c2 =
0.131) = 1.17; rear_focus = 2.59**.

**Prediction check, line by line.**

From the task brief:

1. *"Bernoulli: elongation at 8 m/s falls from > 1.5 at 2 % to < 1.3 at
   20 %."* **Refuted, but not the way it sounds.** The `< 1.3 at 20 %`
   half is trivially true (1.064). The `> 1.5 at 2 %` half is false:
   measured 1.082. Bernoulli was never elongated enough at 2 % for a
   "collapse" to be visible in the first place — at p0 = 0.44 (needed to
   keep the arrival rule alive, see the calibration note) even a 2 %-
   burned Bernoulli fire already has several simultaneously-burning
   neighbours around most of its perimeter, which is exactly the
   saturation mechanism E37 named.
2. *"Arrival: elongation within ± 0.15 across sizes at every wind."*
   **Confirmed, easily.** The largest range in Table 2 is 0.046
   (Bernoulli, calm — arrival's own worst case is 0.018); at every wind
   arrival's own range is ≤ 0.017.
3. *"Default c2 gives LB ≈ 1.6 at 8 m/s (Anderson: 7.9)."* **Refuted.**
   Measured LB at c2 = 0.131, 8 m/s is 1.094, well under the predicted
   1.6 (Anderson's own curve, clamped to 8, gives 7.03 at 8 m/s here, not
   7.9 — a small difference from rounding/clamping, not a discrepancy in
   the formula).
4. *"c2 ≈ 0.3–0.45 needed to approach Anderson at 5 m/s."* **Partly
   confirmed, partly refuted.** c2 = 0.45 is the closest of the four
   tested at every wind, so *more* c2 helps in the right direction, but
   "approach" overstates it: even c2 = 0.45 only reaches 42 % of
   Anderson at 5 m/s and 26 % at 8 m/s.
5. *"No single c2 matches at all three winds because the factor is
   exponential in v."* **Confirmed.** LB/Anderson falls from 0.70 (2
   m/s) to 0.42 (5 m/s) to 0.26 (8 m/s) at the best-performing c2 = 0.45;
   no c2 in the tested range gets close at more than one wind.

From the addendum:

6. *"The rear-focus law gives head:back ≥ 2 already at 0.6 m/s."*
   **Confirmed.** 2.59.
7. *"LB within 20 % of Anderson at 2, 5 and 8 m/s at every size, under
   the arrival rule."* **Confirmed only at 2 m/s (barely); refuted at 5
   and 8 m/s.** LB/Anderson is 0.80 at 2 m/s (20 % short, right at the
   boundary), 0.79 at 5 m/s (21 % short), 0.36 at 8 m/s (64 % short).
   Finding 4 below explains why.
8. *"Under the Bernoulli rule its elongation still collapses with
   size."* **Confirmed, and arrival collapses too** — see finding 4;
   this is not the clean "arrival holds, Bernoulli doesn't" split the
   sentence implies, because the two rules were not both tested at the
   same burn duration in the pre-registered tables (Bernoulli's rows
   above are at duration 5; a supplementary check at rear-focus/8 m/s,
   duration 500 — the value needed for rear-focus to grow past a thin
   spine at all — put both rules on the same footing, and both fell).

**What it means.**

1. **Arrival's own core claim holds.** Direction setting ignition time
   rather than ignition chance really does decouple shape from size on
   a uniform grid: every arrival row in Table 2 stays inside a 0.02-wide
   band, at every wind. This is the mechanism E37 asked for.
2. **Bernoulli's predicted collapse could not be observed, because
   Bernoulli was never elongated in the first place at these
   parameters.** The brief's numeric prediction (> 1.5 at 2 %) assumed
   a small Bernoulli fire is meaningfully stretched before it rounds
   out; at p0 = 0.44 it is already close to round by 2 % burned. A lower
   p0 might show more of a shape at small sizes (E37's own most-elongated
   illumination elites used p0 0.08–0.18), but p0 that low was ruled out
   here because it kills the *arrival* rule's fire before it can be
   compared on the same footing (the calibration note above) — a real
   tension between "low enough to show shape" and "high enough to
   survive," not explored further in this task.
3. **The default wind law is far short of Anderson at every wind
   tested, confirming E30a's premise from a different angle than E41:**
   it is not just too weak to produce a front/back sign (E41's finding),
   it is also too weak to produce the *magnitude* of stretch Anderson's
   curve calls for, even scanning c2 up to 0.45.
4. **The rear-focus law gets the sign right immediately but needs more
   room than a 10 %-burned point ignition gives it to show its full
   magnitude at high wind.** The supplementary rear-focus check (wind 8,
   burn duration 500, checkpoints 2/5/10/20 %) makes this visible
   directly: elongation *starts* far above Anderson's target (8.4 at
   2 % burned — a thin, barely-widened spine) and *falls* toward it as
   the shape thickens (4.98 at 5 %, 2.55 at 10 %, 1.37 at 20 % —
   overshooting past round). The 10 %-size checkpoint this task's Table
   3 reports is a snapshot mid-transition, not a converged shape; a
   later size or a shorter, more moderate burn duration might land much
   closer to Anderson. **v2 update: this "still transitioning" pattern
   turned out to be real physics of the rear-focus law itself, not an
   artifact of v1's heat accumulator or its burn_duration = 500 — see
   Table 2b above, measured under v2 at the pre-registered burn
   duration 5.**
5. **Front speed itself is duration-independent for the arrival rule**
   (Table 1's duration-10 numbers match duration 5 to 3 decimals,
   exactly), unlike Bernoulli's small but real duration sensitivity —
   a direct consequence of the module doc's own claim that arrival's
   head:flank *speed* ratio is `dir[head] / dir[flank]` regardless of
   duration. Arrival is consistently 35–45 % slower than Bernoulli at
   matched p0, since a rate accumulated linearly reaches 1 later than an
   inclusion-exclusion probability saturates.

**Recommendation for E30 (Task 8), as of v1 — superseded by the v2
recommendation above.** Use the **arrival rule** — its core promise
(shape independent of size) is confirmed cleanly and cheaply. For the
wind law, use **rear_focus**: it is the only option tested that gets the
front/back sign E41 needs, and its shape is directionally correct (LB
rises with wind, head:back already exceeds 2 at 0.6 m/s) even though
this task's own 10 %-size measurement under-reports its converged
magnitude at high wind. A `c2` value is not recommended at all under the
exponential law — even its best-performing setting here (0.45) reaches
only 26 % of Anderson at 8 m/s, so E30 should treat the exponential law
as ruled out for matching Anderson rather than pick a "best" `c2` among
options that all fail the same way. If E30 keeps the exponential law for
continuity, `c2 ≈ 0.45` is the least-wrong of the four tested — but that
is a statement about which failure is smallest, not an endorsement.

**Discipline: the Bernoulli path is unchanged.** `step_chunk_bernoulli`
is the pre-existing bit-packed stepper moved verbatim into its own
function, byte-for-byte; the dispatcher only adds a branch on
`params.spread`. Proof, not just claim: the pre-existing wildfire
snapshot/hash stress tests in `cella_lib/tests/long_suite.rs`
(`stress_2d_wildfire_t1/t4/t8`, with and without spotting) pass
unmodified against their stored hashes at 1, 4 and 8 threads, after this
task's changes — those hashes are FNV-1a digests of a 256×256 mixed-fuel
run's final grid state, so any change to the Bernoulli arithmetic, RNG
draw order, or chunking behaviour would have broken them.

**Questions this raises (as of v1).**

- What (rule, law, `c2`) actually reproduces Anderson at a *converged*
  shape, not a 10 %-burned snapshot? **Answered in part by v2**: neither
  law converges to Anderson within the budgets tested; still open.
- Would a lower p0 with a longer burn duration (rather than p0 = 0.44,
  duration 5) let Bernoulli show the small-size elongation the original
  prediction expected, without also killing arrival's fire? **Answered
  by v2**: yes, p0 = 0.12 no longer kills arrival, but Bernoulli still
  did not show the predicted small-size elongation under the exponential
  law (Table 2a) — and died outright under rear-focus (finding 5, v2).
- Does the rear-focus law's shape, plugged into the real six-fire
  scenarios (not a flat grid), actually move the wedge E37 found, the
  way E43's spotting genes did? Open — this is Task 8/E30's own
  question.

**Verdict (v1, superseded).** Finding — the arrival rule's headline
claim holds; the wind-law comparison is a genuine mixed result, reported
as measured.

**Later.** v2, above.