diff --git a/PROJECT.md b/PROJECT.md
index 1d969ed..e67d9ab 100644
--- a/PROJECT.md
+++ b/PROJECT.md
@@ -414,6 +414,36 @@ zombie (now an occasional opponent, `TUNE.racketEvery`, not the every-game fixtu
`RETURNED`, `DOUBLE KILL`) is a footnote to something you already watched happen. Sharing the loud
FACE with the milestone cards is right; sharing their SIZE made every footnote read as a milestone.
+## Session log: a lane is free when its body is gone, not when it is far away (2026-08-16)
+
+A seed-sensitive red suite (`SEED=1234 node test.js` failed on the drill's "spaced *after the walk
+in*" assertion) turned out to be the assertion telling the truth: **two drill bodies genuinely walked
+through each other.** The spawner measured its best-of-9 lane spacing only against bodies with
+`z < drillNearZ` (9 m) — sound for a court where bodies wander, wrong for this one, where a lane is
+HELD for life and speed is stamped at spawn. A body 12 m down still owns its lane; handing that lane
+to a faster arrival does not create a spacing problem at spawn, it books an overtake for ten seconds
+later, mid-court, as two sprites occupying the same metre. The fix is the filter: spacing is measured
+against every live drill body, `drillNearZ` is deleted (it had no other reader). No retry loop, so
+the stream still cannot stall.
+
+Measured, 60 seeds × 150 s of drill each: before, **7 seeds produced overlaps, narrowest clearance
+0.00 m**; after, **zero overlap frames, narrowest 0.46 m** (the test asks for > 0.3). Peak on court
+is still the cap of 7, so the fan is wider, not thinner. Suite green on 20260811 / 1234 / 777 / 99 /
+424242 / 8, and on 299 of seeds 1..300 — the one straggler is unrelated and still open (see below).
+
+**The one straggler the same fuzz found, and the arithmetic to copy for any rate gate here.**
+`SEED=80` failed *"opponent one returns a slid serve: 5/10"*. The receiver is innocent: measured over
+200 trials a seed across eight seeds, the real return rate is **0.85..0.92**. The gate was simply too
+small a sample — asking 6 of 10 at p ≈ 0.88 fails with probability ~0.004, one seed in 230, and 300
+seeds duly produced one. **n is now 40 at the same 60% bar**, which puts the threshold five standard
+deviations below the true rate while still failing instantly if the receiver regresses toward the
+0-in-24 the test was born from. It costs nothing — a returned serve breaks the frame loop early, so
+the whole suite still runs in about a tenth of a second. Green on seeds 1..400.
+
+The general lesson: **a pass/fail gate on a random rate needs its sample size derived from the rate,
+not guessed.** If the true rate is p and the bar is b, n has to put b several standard deviations
+(`sqrt(p(1-p)/n)`) below p, or the gate is a slow coin flip that trains you to re-run the suite.
+
## Session log: THE NETPLAY LOBBY — a link to send, a code to type, a name to be (2026-08-15)
Oliver: *"multiplayer should be easier to setup — a share with link next to the code; also entering
@@ -1000,9 +1030,9 @@ are the ones being called now, which is a better decoy than the old fixed-batch
- **`PLACEMENTS` is deleted along with the marks.** Nothing rushes anywhere and nothing stops, so
there are no shapes to place — the geometry now comes from lane choice at spawn plus the depth the
stream naturally strings bodies out into. That is a net DELETION of the fanciest code in the file.
-- **Spacing is best-of-9 lane sampling** against everyone still in the back half, not a retry loop:
- a hard minimum would silently stall the stream at exactly the moment it is meant to be getting
- harder, which reads as the mode dying.
+- **Spacing is best-of-9 lane sampling** against every live drill body (it measured only the back
+ half until 2026-08-16, see that session log), not a retry loop: a hard minimum would silently stall
+ the stream at exactly the moment it is meant to be getting harder, which reads as the mode dying.
- **A DRILL BODY HOLDS ITS LANE**, and this was the fix the screenshot forced. `updateZombies` homes
bodies onto the player's x once they cross the net — a gore-mode behaviour — so they arrived nicely
spread and then bunched into an unreadable knot right in front of you. Spawn spacing cannot survive
diff --git a/index.html b/index.html
index 6778449..440bd8a 100644
--- a/index.html
+++ b/index.html
@@ -659,6 +659,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') {
-
+