"""Night 13: report the #52 root cause on the tracker. ASCII only.""" import sys sys.path.insert(0, r"C:\git\bt411\scratchpad\night7") import gitea BODY = """**ROOT CAUSE FOUND AND FIXED (2026-08-07, commit `6a96fb6`)** -- but read the "what is NOT proven" section before treating this as closed. ## The defect A replicant cannot start walking between gait-change records. The port's body `case 4` (the task-#64 lockstep twin, mech2.cpp) is an INSERTION sitting between `case 0` and the advance group. In the binary it is a MEMBER of that group -- `FUN_004a5678` @004a5678 reads `case 2,3,`**`4`**`,5,8,...` with no turn block and no speed exit [T1] -- so case 0's fallthrough is meant to land on `Advance()`. The insertion intercepted it. On a replicant that is not a race, it is an identity: * case 0 arms walk iff `standSpeed < bodyTargetSpeed` * the inserted block resets iff `standSpeed < bspd`, and `bspd` **IS** `bodyTargetSpeed` on a replicant Same expression. Arm and reset fire on the same frame, every frame. A peer parked at Standing with a live replicated demand never cycles: `bodyCycleSpeed` stays 0 while position advances from dead reckoning. That is the skate. (Reverse is dead the same way -- both sides test `< ZeroSpeed`.) ## Why it appeared when it did This is the sequel to `e91d447` (#82). Before that commit the replicant branch read the LOCAL mapper's `speedDemand` -- a dead cell on a peer, 0 forever -- so the exit never fired and the fallthrough worked BY ACCIDENT. Fixing the dead cell (correctly) closed the accidental escape hatch, and the trn-lock skate came back as a Standing-lock skate. ## Why masters were unaffected Two reasons, either sufficient: their two tests read DIFFERENT cells (`bodyTargetSpeed` held at last-sent by the gait mirror, vs the live mapper `speedDemand`), so they only stall in the window where those disagree; and the master's body channel runs `mj=0` and writes no joints, so its stall is invisible -- the leg channel, whose case 0 falls through correctly, drives pose and travel. ## The load-bearing detail `mech4.cpp`'s "stand; case 0 walk-begins next tick" is not an aside. A peer's body state is set from `record->legState` only on **type-3 edges**, and ENTERING Standing emits one while LEAVING it does not. Between gait-change records a replicant is REQUIRED to derive walking itself from the replicated demand. The insertion removed that ability. This also explains the shape of the field data: the lock needs a mech holding a STEADY demand, so a mech whose gait keeps changing is continually rescued by records. Night 13's four episodes all carry `bodyTargetSpeed` 39-48 held across 100-400 frames, and each ended when that mech next changed gait. ## Fix `case 0` -> `goto advance_body_normally`, the leg twin's own idiom, restoring the binary's structure without touching the #64/#82 turn logic. `BT_NO_BODY_FALLTHRU=1` reverts. ## Measured (2-node, `scratchpad/night13/skatelock.sh`) | | legacy | fixed | |---|---|---| | STANDING-LOCK seconds | **336 consecutive**, `bspd=39.2324 bts=39.2324` identical every line | **0**, every pass | | master body-Standing samples | 52 | 21 | | turn-in-place | -- | body state 4 x9 / leg state 4 x8, armed in lockstep | ## What is NOT proven [T3] That this accounts for the night-13 episodes. The lock is proven and proven removed; the link to the field symptom is INFERENCE -- a locked peer has `bodyCycleSpeed==0` and never advances its clip, so locked + translating IS the `[skate]` signature by construction -- but no bench caught the two together. Four rigs failed to reproduce the symptom end-to-end. The `[skate]` line now carries `bstate=`, which is the diagnostic tonight's logs lacked. Next playtest settles it: episodes gone -> confirmed; any survivor names its own state. **Correction for the record:** the night-12 `skatebench` "reproductions" (6 episodes, `sk_run.out`) were a DETECTOR ARTIFACT, not this bug. The first detector build tested only `legCycleSpeed==0`, which is NORMAL on a peer -- the body channel poses it -- so it fired on every healthy movement phase. It was corrected the same day to require both channels idle. Old-format lines (`legCycleSpeed=`, no `bodyCyc=`) are not evidence of anything. Related: #130 (Vulture skating) is very likely the same defect -- re-test it against this build before spending separate effort.""" gitea.comment(52, BODY) gitea.comment(130, "Cross-ref: #52's root cause was found and fixed 2026-08-07 (`6a96fb6`) -- a replicant could not " "start walking between gait-change records, so a peer parked at Standing with a live replicated " "demand kept its position advancing with a dead animation channel. That is the same shape as the " "skating reported here. **Re-test this against a build newer than `6a96fb6` before investigating " "separately.** Full write-up in #52.")