Files
BT411/scratchpad/night13/skatelock.sh
T
Joe DiPrimaandClaude Opus 5 6a96fb6420 #52 the peer STANDING-LOCK: case 0's fallthrough was intercepted
A replicant could not start walking between gait-change records.  The port's
body case 4 (the task-#64 lockstep twin) is an INSERTION sitting between case 0
and the advance group; in the binary case 4 is a MEMBER of that group
(FUN_004a5678 @004a5678: case 2,3,4,5,8,... -- no turn block, no speed exit),
so case 0's fallthrough is meant to land on Advance().  The insertion caught it.

On a replicant that is not a race but an identity: case 0 arms walk iff
standSpeed < bodyTargetSpeed, and the inserted block resets iff standSpeed <
bspd -- where bspd IS bodyTargetSpeed for a replicant.  Same expression, so arm
and reset fire on the same frame, forever, and a peer parked at Standing with a
live replicated demand never cycles.  bodyCycleSpeed stays 0 while position
advances from dead reckoning: the skate.

This is the sequel to e91d447 (#82).  Before it the replicant branch read the
dead local mapper cell (0 forever), the exit never fired, and the fallthrough
worked BY ACCIDENT.  Fixing the dead cell closed the escape hatch.

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  336 consecutive locked seconds, bspd=39.2324 bts=39.2324 every line
  fixed   0 locks, every pass
  master body-Standing samples 52 -> 21 (it locked too, invisibly at mj=0)
  turn-in-place intact: pivoter body state 4 x9 / leg state 4 x8, in lockstep

Diagnostics (both keepers): [skate] now carries bstate= -- the field lines
proved "both channels idle" but never named the state, which was the whole
answer; [bodySM]/[peergait] under BT_BODY_SM_LOG instrument the arm->reset pair
and a moving replicant's body channel.

NOT claimed: that this accounts for the night-13 field episodes.  That link is
inference -- locked + translating IS the skate signature by construction, but no
bench caught the two together.  bstate= settles it next playtest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCJQkvq6G2JNrpVbA75tVZ
2026-08-07 00:50:54 -05:00

113 lines
5.5 KiB
Bash

#!/usr/bin/env bash
# =========================================================================
# #52 STANDING-LOCK bench -- the night-13 field composition.
#
# FIELD COMPOSITION (night 13, 4.11.817): a healthy PEER that pivots at
# stand and then accelerates. The peer's turn-arm block parks its body
# channel at Standing on the walk-demand yield (mech4.cpp:2853, "case 0
# walk-begins next tick"); the claim under test is that case 0 CANNOT
# walk-begin on a replicant because the port's inserted turn block resets
# the state case 0 just armed, using the SAME expression.
#
# PROVOCATION: B chases A and holds at BT_GOTO_STOP. Every time A walks
# back out of that radius B re-acquires -- goto turns hard at 0.2 throttle
# (mech4.cpp:4108-4113) = TURNING AT SUB-WALK SPEED, which arms the peer's
# body state 4 -- then the heading aligns, throttle goes to 1.0, and the
# walk-demand yield fires. That is the trap, once per re-acquisition.
#
# READ IT ON A: B's replicant lives on A, so A's log carries [bodySM] and
# [skate] for B. Both nodes carry both gates anyway.
#
# MODE=legacy -> BT_NO_BODY_FALLTHRU=1 (pre-fix path; expect the lock)
# MODE=fixed -> default (expect no lock)
# =========================================================================
set -x
MODE="${1:-fixed}"
DUR="${2:-230}"
. /c/git/bt411/scratchpad/night6/bench_common.sh
cd /c/git/bt411/content || exit 1
taskkill //F //IM btl4.exe > /dev/null 2>&1
sleep 2
rm -f sl_${MODE}_a.log sl_${MODE}_b.log sl_${MODE}_relay.log
bt_assert_player_env
bt_expert_egg MP.EGG SL.EGG
# map=CAVERN on purpose (not the usual grass/day combat default): the lock needs
# a mech holding a STEADY walk demand without its gait changing, and the
# reliable way to get that is a mech pushing into geometry -- throttle up, leg
# SM parked, no gait edges, so no type-3 record ever refreshes the peer. On
# open grass whether the autodriver finds a wall is luck: the first run locked
# for 336 consecutive seconds, the second for 1. Cavern guarantees it, and its
# walls also let the jammed mech SLIDE, which is what turns a lock into a
# visible skate. time=day only so the windows are watchable.
sed -i "s/^map=.*/map=cavern/; s/^time=.*/time=day/; s/^vehicle=.*/vehicle=madcat/" SL.EGG
LEGACY=""
[ "$MODE" = "legacy" ] && LEGACY=1
# ROLES (v2, after the pass-1 miss): the mech we need MOVING WHILE LOCKED is
# the one being observed. v1 made the observer autodrive and it walked into
# the arena wall -- 336 locked seconds but ~zero translation, so the lock
# reproduced and the SKATE (which needs 90 sustained MOVING frames) did not.
# v2 puts the chaser on A: BT_GOTO with a tight stop radius keeps A walking at
# a target that keeps moving, so A gets a stop/turn/walk cycle (the lock entry)
# AND continuous travel (the symptom). B observes; B's own wall-bumping is
# irrelevant because we read A's replicant on B's log.
# v3 = back to the v1 roles, which is the rig that ACTUALLY reproduces.
# v2 (chaser observed) gave zero locks, and that is itself the finding: a mech
# whose gait keeps changing keeps emitting type-3 records, and each one sets the
# peer's body state directly (ReadUpdateRecord), so the peer never has to
# self-arm and never meets case 0. The lock needs the opposite -- a peer PARKED
# at Standing while the master holds a STEADY demand, so no refreshing record
# ever comes. A wall-jammed autodriver is exactly that, which is why v1 locked
# for 336 consecutive seconds. Keep it.
# ---- node B (back window): the OBSERVER -- chases, so it stays engaged ------
(
export BT_GOTO=enemy BT_GOTO_STOP=150 BT_GOTO_LOG=1
export BT_BODY_SM_LOG=1 BT_MP_LOG=1 BT_MATCHLOG=1
[ -n "$LEGACY" ] && export BT_NO_BODY_FALLTHRU=1
bt_launch sl_${MODE}_b.log SL.EGG 0x0C -net 1601
)
sleep 2
# ---- node A (front window): the OBSERVED mech -- steady demand, held up -----
(
export BT_AUTODRIVE=0.7
export BT_BODY_SM_LOG=1 BT_MP_LOG=1 BT_MATCHLOG=1
[ -n "$LEGACY" ] && export BT_NO_BODY_FALLTHRU=1
bt_launch sl_${MODE}_a.log SL.EGG 0x03 -net 1501
)
sleep 5
python ../tools/btconsole.py SL.EGG 127.0.0.1:1501 127.0.0.1:1601 > sl_${MODE}_relay.log 2>&1 &
RELAY=$!
sleep "$DUR"
kill $RELAY 2>/dev/null
bt_kill_ours
sleep 2
# bt_launch's winpid poll can MISS (documented) -- pass 1 orphaned a node that
# then sat holding a -net port. Passes run strictly one at a time here, so a
# blanket sweep is safe and is the only thing that guarantees a clean slate.
taskkill //F //IM btl4.exe > /dev/null 2>&1
sleep 3
echo "=================== MODE=$MODE ==================="
echo "--- THE LOCK: [bodySM] arm->reset pairs (1 Hz throttled) ---"
echo -n "on B (observing A, the chaser): "; grep -ac "STANDING-LOCK" sl_${MODE}_b.log
echo -n "on A (observing B): "; grep -ac "STANDING-LOCK" sl_${MODE}_a.log
grep -a "STANDING-LOCK" sl_${MODE}_b.log | sort -u | head -4
echo
echo "--- THE PEER GAIT: what a MOVING replicant's body channel is doing ---"
echo -n "samples on B: "; grep -ac "peergait" sl_${MODE}_b.log
echo "body states seen while moving (B's view of A):"
grep -a "\[peergait\]" sl_${MODE}_b.log | grep -oaE "bstate=[0-9-]+" | sort | uniq -c | sort -rn | head -10
echo "idle-channel samples (the skate condition):"
grep -ac "IDLE CHANNELS" sl_${MODE}_b.log
echo
echo "--- THE SYMPTOM: [skate] episodes ---"
echo -n "on B: "; grep -ac "SKATING" sl_${MODE}_b.log
grep -a "\[skate\]" sl_${MODE}_b.log | head -6
echo -n "on A: "; grep -ac "SKATING" sl_${MODE}_a.log
echo
echo "--- A's drive cycle (arrive/re-acquire churn = lock entries) ---"
grep -a "\[goto\]" sl_${MODE}_a.log | grep -oaE "arr=[01]" | uniq -c | wc -l