"""Night 13: close #148 as not-a-bug with the measured chain. ASCII only.""" import sys sys.path.insert(0, r"C:\git\bt411\scratchpad\night7") import gitea BODY = """**NOT A BUG -- closing (2026-08-08).** There was nothing to fix here. The bench was lying, and this ticket's own premise was wrong twice over. ## The answer `Entity::Execute` (`ENTITY.cpp:556`, real engine source [T0]) calls `PerformAndWatch` **only** when ```cpp application->GetApplicationState() == Application::RunningMission || application->GetApplicationState() == Application::EndingMission || IsPreRunnable() ``` and otherwise merely `WriteSimulationUpdate()`s. `Entity::DefaultFlags` is `DynamicFlag|MasterInstance` -- **no `PreRunFlag`**. Only `Player` and `Director` add it in their DefaultFlags, and `Mech::Reset` sets it for a reset MASTER ("a reset master must tick"). A **replicant mech never gets it.** So a peer mech performs **zero** subsystem ticks until the round actually starts, no matter how much correctly-replicated data is arriving for it. Measured on the observer node: ``` 235 [perf-first] mech 3:161 master <- own mech, immediately 402 [torso-rec-rx] <- peer's torso records start arriving 2754 [perf-first] mech 2:55 REPLICANT <- peer's FIRST performance 2758 [torso] PushTwist COPY <- its torso ticks 4 lines later 2761 [ent-exec] state=5 <- RunningMission ``` The peer starts performing exactly at the RunningMission transition. That is the engine doing what it says it does. ## So the symptom was a BENCH ARTIFACT `BT_AUTOFIRE` starts shooting immediately, during `WaitingForLaunch` -- something no player can do in a real match. Those leading salvos measured a peer whose torso, gait and subsystems had never run. Every `ZZZZ...XXXX` prefix in this investigation was that, and the first `X` lands within a few lines of the state transition. **#141 is unaffected and stays fixed** -- its segment-cache defect was real and mid-match. ## Ruled out along the way (all measured, all recorded so nobody repeats them) * **The record cadence is authentic.** My "only 13 records in 5 minutes" premise was wrong. The payloads are the sweep EXTREMES with `rate` flipping sign at each one -- the master sends on **rate change** and the peer dead-reckons `atUpd + rate * elapsed` between them. 12 records for 12 direction reversals is correct, not starved. * **The `ComputeTargetTwist` clamp.** The copy's limits load correctly (`limL=2.44346 limR=-2.44346 enab=1`), so `Min/Max` was not pinning `targetTwist` to zero. * **The torso's own executable flag.** `Entity::Perform` picks its predicate by instance (`IsNonReplicantExecutable` vs `IsReplicantExecutable`, differing on `|| lastUpdate >= lastPerformance`), and Mech's tick loop had dropped that branch. Restoring it (`f36f013`) is a genuine fidelity fix and is kept -- but it moved this bug by nothing. * **The scheduler.** The replicant entity IS offered to the performer with `executable=1` from line 171, ~2500 lines before its first `PerformAndWatch`. The gate was inside `Execute`, not in who gets offered. ## What came out of it * `[perf-first]` -- a one-shot per-mech receipt naming entity ID + instance at a mech's first performance. Every other per-frame receipt in mech4 is anonymous, which is exactly why this took so long to see in a 2-node log. * `[torso-copy]` now prints `limL/limR/enab`; `[launchframe]` prints the shooter's live torso twist. * **Gotcha #29** in `context/reconstruction-gotchas.md`: judge a 2-node bench by PREFIX vs INTERLEAVED, never by raw percentage; check `[ent-exec] state=` before suspecting replication. `missileframe.sh` carries the same warning inline. ## Still worth checking separately `#37` (MadCat torso BACKWARDS) and `#70` (torso twist stops after respawn) were flagged here as possibly sharing a cause. They do **not** share this one -- it is not a defect. They should be re-tested against the #141 segment-cache fix instead, which is a real mid-match change.""" gitea.comment(148, BODY) gitea.call("/issues/148", method="PATCH", payload={"state": "closed"}) print("closed #148")