From 202ebbf84ad2c966a26f7f50691e360573698d3a Mon Sep 17 00:00:00 2001 From: Joe DiPrima Date: Sun, 2 Aug 2026 01:15:45 -0500 Subject: [PATCH] KB + tracker: night-9 playtest (build 4.11.693) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIRMED FIXED and closed: #95 (missile salvo damage), #97 (leak gauge 3 levels), #98 (loop-6 lamp), #99 (leak alarm audible). #95 deserves a note: a tester reported "LRMs still doing 3.3 damage", but that is the PER-MISSILE figure. 382 impacts across three 693 logs show the cluster being delivered correctly -- LRM20 3.25 x burst 5..20 (total up to 65), LRM15 3.33 x 3..15, LRM10 3.50 x 2..10 -- and those burst ranges match the binary's Random(n)+n/4 roll exactly. #32 REOPENED. The pooling change removed the alloc/free churn (the ~10% frame time is real) but every 693 field log still shows ACQUIRE FAILED (requested=4 live=240 pooled=240 free=0 ...) 19,182 fails (Sauron) / 15,333 (Rajel) / 6,818 (Ronin), high-water 225 of 240, starting ~1% into every session. `free=0` is the diagnosis: sources are never returned. This is precisely the risk flagged when the fix landed -- retention, not pool size. Players called audio "fine" the same night; the log disagrees. #84 still present, but the surviving explosion is a DIFFERENT mechanism from the bundled one that was fixed: "where the mech was when I fired" is the stale fire-time aim point already recorded as an open in rendering.md, i.e. an MP visual, not the message-manager path. Needs a two-node bench logging the OBSERVER -- the earlier two-node run verified damage delivery but never the visual. New: #108 ghost mech (peer never sees the death; shots still register, so it is a visual/replication desync, the inverse of #94), #109 panic/eject, #110 arm parent/child linkage (gun pod survives its mount -- three chassis, both arms; parentArtifactZone exists but only drives LOD), #111 death warp bubble shown to peers, #112 AFC shotgun spread (may be authentic Ultra-AFC burst), #113 LRM burn duration, #114 missile smoke as a smoke screen, #115 hot box, #116 Standard-vs- Expert heat gating, #117 per-chassis cockpit bounce. ⚠ A MEMBER OF THE ORIGINAL TEAM IS NOW TESTING -- "Ronin" (log user torak). He worked on the 1995 game and has played the pods recently. Treat his pod comparisons as primary evidence above reconstruction inference. First two contributions recorded in the KB: per-chassis cockpit bounce was deliberate, and NARC likely shipped as data without an implementation. ⚠ Conn Man ran the OLD build (.674) for all seven of his sessions, so his reports are not evidence about 693. His arm-linkage audit is still valuable -- nothing in 693 touched that path. Co-Authored-By: Claude Opus 5 (1M context) --- context/open-questions.md | 48 ++++++++++++++ scratchpad/night9/housekeep.py | 105 +++++++++++++++++++++++++++++ scratchpad/night9/newbugs.py | 116 +++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 scratchpad/night9/housekeep.py create mode 100644 scratchpad/night9/newbugs.py diff --git a/context/open-questions.md b/context/open-questions.md index d396db8..666d59f 100644 --- a/context/open-questions.md +++ b/context/open-questions.md @@ -1016,3 +1016,51 @@ heat / FailureTemperature scaling is too aggressive, or the roach-motel (no reco falls) is the wrong reading. NEXT: audit the SRM6 heatPerShot vs FailureTemperature vs the mech's dissipation, and whether the binary's gate-1 FailureHeat is a LATCH or clears when heat drops. Trace it live with `BT_AMMO_LOG=1` (+ `BT_PROJ_LOG=1` for per-tick heat/recoil). + +## Night-9 playtest intelligence (build 4.11.693, 2026-08-01) + +**A MEMBER OF THE ORIGINAL TEAM IS NOW TESTING.** "Ronin" (log user `torak`, +machine TOWER_OF_TERROR) worked on the 1995 game and has played the pods +recently, including in Japan. He is a **primary source** — treat his comparisons +against the pods as high-value evidence, above reconstruction inference. Two +contributions already: + * **Per-chassis cockpit BOUNCE was deliberate** [T1, testimony]: "all of the + chassis have different cockpit 'bounce' — this was made so to help give the + 'mechs some individuality". If our port applies one shared bounce curve, a + deliberate identity cue is missing. (Gitea #117; `jointshakey` is the likely + carrier — its pick sphere radius already differs per chassis.) + * **NARC may never have been implemented** [T3, recollection]: the loadout data + shipped but he does not recall the weapon being built. Squares with what we + see — an authored record that resolves with no distinct behaviour to + reconstruct. Mike L. could confirm. (Gitea #104.) + +**AUDIO SOURCE POOLING IS NOT FIXED — retention, not churn, is the wall** [T2]. +The `#32` pooling change removed the per-sound `alGenSources`/`alDeleteSources` +churn (a real ~10% frame-time win) but every 693 field log still shows + `ACQUIRE FAILED (requested=4 live=240 pooled=240 free=0 ...)` +19,182 fails (Sauron) / 15,333 (Rajel) / 6,818 (Ronin), high-water **225 of 240**, +starting ~1% into the session. `free=0` is the diagnosis: the pool fills and never +returns a source. Each audio component reserves a SourceSet of up to 25 voices and +holds it to entity teardown, so peak demand scales with live audio COMPONENTS, not +audible sounds. A bigger cap only delays the wall. ⚠ Players reported audio as +"working fine" the same night — subjective impressions do not clear this class of +bug; read the log. + +**Reading the missile impact line.** `[projectile] IMPACT damage=X ... burst=N` +prints X = **per missile**; the delivered total is `X * N`. A tester reading X +alone will report LRMs as doing ~3.3 damage when the salvo is landing up to 65. +Field-verified across 382 impacts: LRM20 3.25 x 5..20, LRM15 3.33 x 3..15, +LRM10 3.50 x 2..10 — burst ranges matching the binary's `Random(n) + n/4` roll +exactly. + +**Weapon damage values are published** in the original manuals (Oracle: "that is +published ... Manuals had that info and it was accurate") — but NOT in the 4.0 +manual we have archived (confirmed by Lynx). A later manual, or the wayback +capture of the original site's final build notes, would give hard per-weapon +numbers to test the authored data against. Worth chasing: it converts weapon +tuning from taste into arithmetic. + +**Standard vs Expert heat is unsettled** and it contaminates every heat report: +Conn Man saw heat build-up in Standard while Rajel expected none there. See +Gitea #116 and `experience-levels.md`. + diff --git a/scratchpad/night9/housekeep.py b/scratchpad/night9/housekeep.py new file mode 100644 index 0000000..6c0c116 --- /dev/null +++ b/scratchpad/night9/housekeep.py @@ -0,0 +1,105 @@ +"""Night-9 (build 4.11.693) bug-report housekeeping.""" +import sys +sys.path.insert(0, r"C:\git\bt411\scratchpad\night7") +from gitea import comment, close, create, call + +BUG, HUD, AUDIO, INVESTIGATE, AWAIT = 1, None, None, None, 8 # only bug/await ids are known-good + +# ---------------------------------------------------------------- CONFIRMED FIXED +close(95, """**VERIFIED FIXED from the field logs — closing.** + +@RajelAran reported *"LRMs still seem to be doing 3.3 damage, at least the LRM10"*. That number is the **per-missile** figure, not the delivered total. The 4.11.693 logs carry the burst count on every impact, and the cluster is being applied correctly: + +| weapon | per missile | burst range observed | total delivered | +|---|---|---|---| +| LRM20 | 3.25 | 5 .. 20 | 16.2 .. **65.0** | +| LRM15 | 3.33 | 3 .. 15 | 10.0 .. **50.0** | +| LRM10 | 3.50 | 2 .. 10 | 7.0 .. **35.0** | +| SRM (8.75) | 8.75 | 1 .. 4 | 8.8 .. **35.0** | +| direct fire | 7 / 13 / 25 | 1 | unchanged | + +382 impacts across Rajel's, Sauron's and Ronin's logs. The observed burst ranges match the binary's authentic `Random(n) + n/4` roll **exactly** — n=20 gives 5..20, n=15 gives 3..15, n=10 gives 2..10, n=4 gives 1..4. So a full LRM20 salvo now lands 65 points, up from the 3.25 it delivered before the fix. + +The `[projectile] IMPACT damage=X ... burst=N` line shows **X = per missile**; total is `X * N`. Worth knowing when reading logs.""") + +close(97, """**CONFIRMED FIXED by two testers — closing.** + +@Oracle: *"observed a stage 3 coolant leak"* (twice), then *"I experienced a three stage coolant leak in the new build, so I'll call that fixed"*. +@RajelAran: *"#97 seems to be fixed, I got tier-3 coolant leak"*. + +Cause was the dropped x87 normalisation around `__ftol` in `BitMapInverseWipe::Execute` (KB gotcha #19): the gauge rounded the raw leak rate instead of `round(fullWidth * rate / fullScale)`, so it could only ever show one triangle.""") + +close(98, """**CONFIRMED FIXED — closing.** + +@RajelAran: *"#98 - cooling loop 6 lamp flashes"*. + +Root cause was a 0-based bound (`n >= 0 && n < 6`) on a 1-based index, so condenser 6 was rejected and annunciated nothing. Loops 1-5 were never broken. The lamp table itself was byte-accurate all along and is now pinned to the image by `scratchpad/night8/lamptable_check.py`.""") + +close(99, """**Audible alarm CONFIRMED WORKING — closing this issue.** + +@RajelAran: *"#32 and #99 - audio seems to be working fine"*. + +Both halves are now settled: +- **No alarm at all** — was the OpenAL source starvation, not a missing feature. The alarm chain is fully implemented and was traced firing end to end (`8daac37`). +- **Does not re-assert for a second leak** — verified AUTHENTIC in the arcade image: `ReceiveControl @0043d3a4` case 2 is an unconditional `call StopSequence @0043d2d4`, which tests only its own `isRunning` with no refcount. 19 subsystems share one alarm sequence, so any one clearing its leak stops it. The cabinet did this too. + +⚠ Note the audio pool is **not** actually healthy — see #32, which I am reopening with the log evidence. The alarm being audible tonight does not mean source starvation is solved.""") + +# ---------------------------------------------------------------- NOT FIXED (escalate) +comment(32, """**REOPENING — the field logs say this is NOT fixed. The pooling change removed the churn but replaced it with permanent retention.** + +@RajelAran said *"audio seems to be working fine"*, and subjectively it may have been. Objectively, every 4.11.693 log is still saturated: + +| player | build | ACQUIRE FAILED | pool high-water | +|---|---|---|---| +| Sauron | 693 | **19,182** | 225 of 240 | +| Rajel (part 1) | 693 | **15,333** | 225 of 240 | +| Ronin | 693 | **6,818** | 225 of 240 | +| Conn Man | 674 (old) | 13,238 | — | + +The failure line has changed shape, and that is the diagnosis: + +``` +[audio] ACQUIRE FAILED (requested=4 live=240 pooled=240 free=0 fails=N) +``` + +`pooled=240 free=0` — the pool fills to its cap and **never returns a source to the free list**. Before the fix it was `live=256` churning against the OpenAL driver limit; now we allocate 240 once and hold all of them. The alloc/free churn is genuinely gone (that bought ~10% frame time, which is real and worth keeping), but availability is no better — and it starts **~1% into every session** and continues to 94-99%. + +This is exactly the risk flagged when the fix landed: *"if next session's logs show that climbing past ~200, the remaining work is RETENTION — reclaiming sources from finished one-shots instead of holding them to entity teardown — not a bigger pool."* High-water 225 of 240 with `free=0` confirms it. + +**The real fix is retention**, not a bigger cap: each audio component reserves a SourceSet of up to 25 voices and holds it until entity teardown, so peak demand scales with the number of live audio components rather than with audible sounds. Raising the cap would just delay the wall.""") +call("/issues/32", "PATCH", {"state": "open"}) +print("REOPENED #32") + +comment(84, """**Still present in 4.11.693 — and the surviving explosion is a DIFFERENT mechanism from the one that was fixed.** + +@RajelAran: *"still getting the double missile explosion - once where the mech was when I fired and on the expected impact"*. @Conn Man saw it too (though he ran the old .674 build all night, so his sighting is not independent evidence for 693). + +What was fixed in `3b7c19c` was the **message manager's bundled explosion** — projectiles now dispatch damage directly at the victim, as the binary does, so they no longer touch the consolidation path that produced a second blast. Verified on the bench: 10 projectile impacts, zero bundled explosions. + +The description now points somewhere else. *"Where the mech was when I fired"* is a **stale fire-time aim point**, which is a known open already recorded in the KB (`context/rendering.md`): *"replicant visual salvo chases the stale fire-time aim point (no re-lead without a target handle)"*. That is a **multiplayer visual** issue — the salvo re-simulated on another node flies at where the target was at launch, detonating there, while the authoritative impact happens at the real position. + +That also likely explains @Ronin's separate report that missile smoke *"effectively functioned as a smoke screen"* — two detonations per missile means roughly double the smoke. + +**What would settle it:** a two-node bench with projectile logging on the OBSERVER, not the shooter. The earlier two-node run verified cross-pod *damage* delivery but never looked at the visual. That is the next step.""") + +comment(96, """**Halving confirmed; still over-tuned per the testers.** + +@RajelAran: *"confirm #96 - myomer heat seems to be halved-ish"*. +@Oracle: *"The myomer heat was toned down and still seems like too much heat generation rate for seek 3"* ... *"I trust it was half, but will need further tuning"*. + +So the frame-rate normalisation landed as intended and the remaining gap is calibration, not mechanism. Two things bear on it: + +1. **The reference rate is not proven.** The kinetic term is scaled by `dt * 30`, and 30 Hz is the documented i860 **board** frame; the host simulation rate was never established, and there is no fixed-timestep constant anywhere in the image. If the host ticked slower than 30 Hz we are still over. **`BT_MYO_HZ=` retunes this live with no rebuild** — @Oracle, if you want to experiment, lower values mean less heat (try 20 or 15). + +2. @Oracle's *"I wish there was a more objective reference for myomer heat rate"* is the right instinct, and it is now partly answerable: the myomer record is **identical on every chassis** (velEff 0.995, accEff 0.8, gears 3000/5000/7000/9999, degradeT 1000, failT 2000, thermalMass 250000), and so is every cooling parameter. Per-mech differences come only from **mass and speed**, through `heat ~ m*v^2`. + +Also relevant: @RajelAran noted *"we're testing on standard, not expert, so heat isn't expected here"*, while @Conn Man reported *"some of us in Standard mode were still experiencing heat build up"*. Those conflict, and it matters for interpreting any heat report — filed separately.""") + +comment(104, """**Provenance from @Ronin, who worked on the original:** + +> *"I don't think that narcs were ever implemented, as I don't recall talking/working with Mike L. on these at the time, so I suspect that they may have been 'cool if we can get this working' and listed as a hope on the 'mech weapons. However, in order to confirm this Mike will have to be contacted."* + +@VGL Lynx counters that the Mad Dog loads with NARC on the missile MFD and that it appears in the final build notes on the original site. + +Both can be true: the **loadout data shipped** (which is why we render it) while the **weapon behaviour was never implemented**. That matches what we see — the authored record exists and resolves, and there is no distinct NARC behaviour to reconstruct. Worth confirming with Mike L. if a contact is possible.""") diff --git a/scratchpad/night9/newbugs.py b/scratchpad/night9/newbugs.py new file mode 100644 index 0000000..531867b --- /dev/null +++ b/scratchpad/night9/newbugs.py @@ -0,0 +1,116 @@ +"""Night-9 (build 4.11.693) new bug registrations.""" +import sys +sys.path.insert(0, r"C:\git\bt411\scratchpad\night7") +from gitea import create + +SRC = ("Source: playtest night 9 (2026-08-01, build **4.11.693 (5a1a407)**), Discord #play-testing.\n" + "Testers: VGL Lynx, Oracle, RajelAran, SAURON (Michael), Conn Man, Draco, " + "**Ronin (NEW -- worked on the ORIGINAL game)**.\n\n") + +create("Ghost mech returned: a dead mech is not removed/respawned on a peer's screen -- everyone shoots thin air", +SRC + """@VGL Lynx: *"Ghost mechs. Returned today."* +@RajelAran: *"there was one instance where a mech did not explode/respawn on my screen, I saw everyone shooting at thin air / **they were hitting something though**"*. + +Rajel's second clause is the important one: the other players' shots were **registering**, so the mech existed and was being hit — his client was rendering it in the wrong place / wrong state, not failing to simulate it. That makes this a **replication/visual** desync, not a dead-entity leak. + +Distinct from the two closed ghost issues: +- #81 was the local player respawning as sliding wreckage. +- #94 was a peer seeing a LIVE mech as wreckage after a collision (zone-less death), fixed by the `MechDeathHandler::Tick` exit-edge watcher. + +This one is the inverse of #94: the peer never sees the death **at all**. Worth checking whether the death record simply never reached that node, or arrived and was dropped — `MovementMode` death modes {2,9} ride the record header, and `simulationState` rides every record. + +Logs staged: `rajel_GAMERSLAB_693_part1/2`, `sauron_XIAOLONG_693`, `torak_TOWEROFTERROR_693`.""", +[1, 6]) + +create("PANIC / EJECT does nothing", +SRC + """@VGL Lynx: *"Confirmed, PANIC/Eject not working."* + +The panic/eject control produces no effect. Needs the input path traced (CONTROLS.MAP binding -> handler) and then whatever the binary does on eject reconstructed. Check first whether the button even reaches a handler, since several eng-page/console buttons are still unreconstructed (#14, #53).""", +[1]) + +create("Destroying an upper arm leaves the gun pod alive and still firing (no parent/child zone linkage)", +SRC + """@VGL Lynx: *"Arms do not seem to have parent/daughter linkage. You can destroy an upper arm, and the gun pod is intact and can still fire."* + +@Conn Man ran a methodical audit and confirmed it (note: **on build .674**, but nothing in .693 touched this): +> *"Test on Avatar, Sunder, and Vulture. Tested both legs on each ... Tested firing on upper arm sections until they were destroyed. The weapon pods on those arms were still functional until also destroying the lower arm sections. Same behavior on both left and right arms for all three 'mechs."* + +Three chassis, both arms, reproducible — a strong report. + +**Where to look:** the linkage exists structurally. `Mech__DamageZone` carries a `parentArtifactZone` socket and children register into it (`mechdmg.cpp`, `child->parentArtifactZone.Add(this)`). But on damage the only thing done with the parent is `parent->UpdateLODDamageLevel()` — a **level-of-detail** update. There appears to be no propagation of DESTRUCTION from a parent zone to its children, which is exactly this symptom. + +Related: #86 (destroyed weapons could still fire) fixed the case where the WEAPON itself is destroyed; this is the case where the weapon is healthy but its mount is gone. + +Same audit also confirmed a fix, worth recording: *"Confirmed that the correct gimped leg animation played after the destruction of the appropriate leg."*""", +[1]) + +create("Dying mech shows the translocation warp bubble to OTHER players -- peers should just see the explosion", +SRC + """@VGL Lynx: *"Other players see a dying mech's blue bubble as they die. It should not be reading like an explosion shockwave. Mech death should just be the big boom from other POV."* +Confirmed in follow-up: warp bubble **only** for the mech that is dying, and only wrong from the observer's view. + +So the tsphere translocation warp (`context/translocation-warp.md`) is being played on the **replicant**, where the original showed only the death explosion. The warp is the first-person respawn effect for the pilot being translocated, not a world effect other pilots should see. + +Likely the same class as #94 — an effect fired on the peer path that should be viewpoint-only. Check the instance test around `BTStartWarpEffect` (`mechdmg.cpp`): the un-wreck path deliberately gates on `Entity::ReplicantInstance`, so the gating convention exists; the death-warp path may be missing an equivalent viewpoint check.""", +[1, 6]) + +create("AFC rounds spread damage across multiple hit zones (shotgun) instead of landing as one projectile", +SRC + """@VGL Lynx: *"Confirmed that AFC are doing shotgun. Area damage, not single projectile damage. Can see AFC damage in multiple hit zones."* + +@Draco pushes back, and may be right: +> *"I dont recall for sure which mechs have what type of AFC but ultra afc should register kinda like shot gun because there are 2 rounds firing correct? so lead is important to get both rounds on same panel"* + +So the question is whether this is a defect or authentic Ultra-AFC behaviour. + +**Note this is the same mechanism as the (correct) missile cluster.** A `burstCount > 1` damage record is applied N times with the hit zone **re-rolled per burst** (binary `@0x4a0439`), which by design sprays across zones. Tonight's logs show an 8.75-per-round weapon delivering burst 1..4 — i.e. something IS bursting up to 4. If AFC is authored with a burst count, the spread is authentic and the only question is the count. + +**Next step:** dump the authored `burstCount` for each AFC variant and compare against the published weapon table @Oracle mentioned; that settles defect-vs-authentic without guessing.""", +[2]) + +create("LRM burn / hit state lasts too long", +SRC + """@VGL Lynx: *"LRM burn/hit state is now too long."* + +Follows the missile thrust work (#84) which gave missiles a real burn phase. The authored burn durations were read from the BTL4.RES type-15 model records: SRM 2.5s @600, **LRM 10s @300**, Streak 3s @300, NARC 10s @300. + +A 10-second LRM burn is what the data says, but it is long, and it is worth re-checking whether that field is a burn DURATION or something else (e.g. a range/lifetime cap) before treating the authored value as settled. Tonight's logs show `burnLeft` values around 8.9-9.7 on impact, i.e. most of the 10s budget unused at contact — consistent with the field being a lifetime cap rather than a thrust duration.""", +[2]) + +create("Missile impact smoke is far too thick and long-lived -- acts as a smoke screen from the cockpit", +SRC + """@Ronin — **who played the original pods, including in Japan last year** — on 4.11.693: +> *"When I got hit with missiles, the smoke emitted as a result seemed to last A LOT longer and thicker than what I experienced in the pods (this is from the cockpit view), so much so that it effectively functioned as a smoke screen - blocking my view of the battlefield. Again, much different from what I experienced in the pods."* + +@RajelAran: *"that may be related to bug #84, the double missile explosion"* — plausible, since two detonations per missile is roughly double the smoke. + +Two other known contributors, both already recorded as open in `context/rendering.md`: +- particle **trail density is frame-rate dependent** (2/frame at 60fps = 2x pod density) — the same bug class as the myomer heat term, and untouched; +- the i860 board ran its particle heat model per **30 Hz** frame. + +So there are at least three candidate multipliers stacking. Worth fixing the frame-rate dependence first since it is a known defect with a known shape, then re-asking @Ronin. + +⚠ @Ronin is a first-hand reference for what the pods looked like — his comparisons are high-value evidence.""", +[1]) + +create("Hot box / target select not connected", +SRC + """@VGL Lynx: *"Hot box/target select not connected."* + +Consistent with an earlier note that the hot box is not functional (mentioned alongside the sensor-panel work, #105). Registering separately so it is not lost inside the sensor issue.""", +[1]) + +create("Standard mode: players report heat build-up where none is expected", +SRC + """@Conn Man: *"Some of us in Standard mode were still experiencing heat build up, including me."* (his session ran on **.674**) +@RajelAran, the same evening: *"well we're testing on standard, not expert, so heat isn't expected here"*. + +These two statements conflict, and the answer matters for reading every heat report we get. + +`context/experience-levels.md` records the heat model as gated by experience level, with novice gates. Needs a definitive answer to: **which heat sources are active in Standard vs Expert?** — specifically whether myomer drive heat, weapon heat and coolant leaks are each gated. `HeatSink::GetStatusFlags` already gates the leak bit on `HeatModelActive()`, so at least one gate exists; the question is whether drive heat honours the same gate. + +Cheap to settle on the bench: run the same profile at each experience level and diff the temperature traces.""", +[2]) + +create("KB: per-chassis cockpit BOUNCE was deliberate -- each chassis has its own (from Ronin, original team)", +SRC + """Not a bug — durable design knowledge from @Ronin, who worked on the original: + +> *"Also, in interest of making sure that all 'lost' info is shared or assumed to be known: you may all be aware of this, but all of the chassis have different cockpit 'bounce' - this was made so to help give the 'mechs some individuality."* + +So the first-person cockpit shake/bob is **per-chassis authored**, not a single shared curve, and it was an intentional identity cue. Worth checking what our port does — if we apply one shared bounce, every mech feels the same from the seat and a deliberate piece of the original's character is missing. + +Relevant files: `context/cockpit-view.md` (the eyepoint camera), `context/locomotion.md` (gait). The skeleton's `jointshakey` segment is the obvious candidate for where per-chassis bounce data would live — note its pick sphere radius already differs per chassis (Loki 2.767 vs Thor 2.561), so the segment is not shared.""", +[2])