j140 fix: a pre-rename .env must not admit the disabled BitBrain net gun

TR_RACK_BITBRAIN=both is what the owner's live .env carries, and with the new
ADE+SBC gun registered at id 17 under the SAME rack name that value was also
landing on id 17 - so a gun that was not enabled (TR_BITBRAIN_NET unset) was
admitted into the rack and its placeholder predictions were pushed into the
shared VirtualTracker ring, which shifts every other gun's learning order.

While the namespace is LEGACY, loadRackMembership now skips id 17's
TR_RACK_BITBRAIN entirely, so that value addresses ONLY the gun it always
addressed (LEADGAIN, id 16). ModularBot additionally gates admission on
BitbrainNetGun.gunAdmitted(), and test_bitbrain_net pins the truth table: over
6 (rack, switch) settings there is NO configuration that admits the gun while
leaving it disabled.

Guards: test_env_report 25, test_rack_membership 49 (was 48; the revert
one-liner now sets TR_BITBRAIN_NET=1 and one truth-table check was added),
test_tm_pattern_registration 20, test_bitbrain 56, test_gun_harness 39,
test_tfil_commit_env 30, test_lead_gain_registration 13,
test_lead_gain_legacy 24, test_bitbrain_net 44.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-26 16:54:43 +02:00
parent e9302bc9f6
commit 59c5af0499
7 changed files with 89 additions and 13 deletions
+12
View File
@@ -586,6 +586,10 @@ proc sbcBytes*(g: BitbrainNetGun): int =
proc predict*(g: var BitbrainNetGun, state: WorldState,
bulletSpeed: float): GunPrediction =
g.ensureInit()
## A disabled gun must never be called: ModularBot gates admission on
## `g.enabled`, and this branch is only a belt-and-braces guard. It returns
## the CURRENT position (a valid, if useless, prediction) rather than anything
## that could poison a shared tracker.
if not g.enabled: return GunPrediction(x: state.enemyX, y: state.enemyY)
if state.tick < g.lastTick: g.resetRoundState()
if state.tick != g.lastTick:
@@ -665,3 +669,11 @@ proc evidenceFor*(g: var BitbrainNetGun, input: openArray[uint8], klass: int): i
## every counted learn, so it is the observable that proves the counted mode
## is accumulating rather than just flipping bits.
g.net.infer(input).counts[klass]
proc gunAdmitted*(g: BitbrainNetGun, rackAdmitted: bool): bool {.inline.} =
## The FULL admission predicate for rack id 17: the rack table AND this gun's
## own master switch. Both default off, so an incomplete configuration is
## exactly as inert as an unset one — which matters, because a pre-rename
## `.env` still carries `TR_RACK_BITBRAIN=both` and must not be able to push a
## disabled gun's placeholder predictions into the shared VirtualTracker ring.
rackAdmitted and g.enabled