fix(h3d-carve-4): export _bcLoadSettings + _bcFfIdx; add caller-coverage test (Creed HIGH)

screen.js H/P-section render path at lines 15396-15402 calls _bcLoadSettings()
and _bcFfIdx() (3×) — both moved to bc-panel.js in cut 4 but omitted from the
export list.  Browser: first render() after bcCtrl creation → ReferenceError;
seek/loop fast-forward index also dead.  Suite was green because no test
executed the butterchurn render path.

Fix:
- export _bcLoadSettings and _bcFfIdx from bc-panel.js
- add both to the tagged import in screen.js

Class-killer (test 12 — generic, not instance-specific):
  Extracts all exports from bc-panel.js, all imports in screen.js's
  bc-panel.js import clause, then asserts no exported symbol appears as a
  bare reference in the screen.js IIFE body without being imported.
  Generic: adding a new export + new caller without updating the import → RED.

Own grep (audit):
  grep (non-comment lines, all private _bc* names from bc-panel.js):
    _bcLoadSettings  1 hit  (line 15396)
    _bcFfIdx         3 hits (lines 15400-15402)
    all others: 0 hits
  Only the two symbols Creed found.

Mutation-verify:
  remove _bcLoadSettings from screen.js import →
    node --test tests/js/highway_3d_bc_panel.test.js
    tests 12   pass 11   fail 1   (test 12 RED)  ✓
  restore →
    node --test tests/js/highway_3d*.test.js plugins/highway_3d/tests/*.test.js
    tests 222   pass 222   fail 0               ✓ GREEN

Plan §8 amended: exports 2→4 with dated note.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014uZ169yfoFYArXz962g7KW
This commit is contained in:
byrongamatos
2026-09-05 09:03:10 +02:00
co-authored by Claude Sonnet 4.6
parent 81b366746e
commit f69c544eea
3 changed files with 48 additions and 4 deletions
+2 -2
View File
@@ -9,7 +9,7 @@
import { geoFretX, dZ, slideTrailEnd, camBaseDistU, camLowFretPullbackU, computeBPM, _makeGaussTex, RENDER_ORDER_LAYER_STACK, RENDER_ORDER_LAYER_INDEX, RENDER_ORDER_AT_Z_ZERO, RENDER_ORDER_FAR_CLAMP, renderOrderForLayerAtZ, _noteKey, lowerBoundT, hwyFirstRelevantFrettedTime, geoFretMid } from './src/geometry.js'; // h3d-carve-1b
import { loadThree, T } from './src/three-loader.js'; // h3d-carve-2
import { _h3dHexToInt, _clampByteI, _darkenInt, _lightenInt, resolveStringCount as _resolveStringCountBase, _NOTE_NAMES_SHARP, _BASE_OPEN_MIDI_BASS4, _BASE_OPEN_MIDI_BASS5, _BASE_OPEN_MIDI_GUITAR6, _BASE_OPEN_MIDI_GUITAR7, _BASE_OPEN_MIDI_GUITAR8, _baseOpenStringMidis, _midiToPitchLabel, _openStringPitchLabelsForTuning as _openStringPitchLabelsForTuningBase, _ssActive, _ssIsCanvasFocused } from './src/utils.js'; // h3d-carve-3
import { _bcIsDesktop, _bcCreateController } from './src/bc-panel.js'; // h3d-carve-4
import { _bcIsDesktop, _bcCreateController, _bcLoadSettings, _bcFfIdx } from './src/bc-panel.js'; // h3d-carve-4
(function () {
'use strict';
@@ -25,7 +25,7 @@ import { _bcIsDesktop, _bcCreateController } from './src/bc-panel.js'; // h3d-ca
// THREE_URL / THREE_CDN — h3d-carve-4: dead code (three-loader.js owns its own URLs). Tombstoned.
// ── B-section: Butterchurn control panel — h3d-carve-4 ──────────────
// Moved to src/bc-panel.js. Imports: _bcIsDesktop, _bcCreateController.
// Moved to src/bc-panel.js. Imports: _bcIsDesktop, _bcCreateController, _bcLoadSettings, _bcFfIdx.
// window.h3dBcApplySettings assigned at bc-panel.js module scope (R5).
// ────────────────────────────────────────────────────────────────────
// TOMBSTONE: BC_VENDOR, BC_FRAME, BC_WORKLET, _bcMeters, BC_BTN — h3d-carve-4: moved to src/bc-panel.js.
+2 -2
View File
@@ -47,7 +47,7 @@ export function _bcIsDesktop() {
// Position at the first entry whose time is >= ct (strict <), so an event
// landing exactly on the seek/loop target time is still fired by the update
// walkers (which consume `<= ct`) instead of being skipped past here.
function _bcFfIdx(arr, ct, key) { if (!arr) return 0; let i = 0; while (i < arr.length && (arr[i][key] || 0) < ct) i++; return i; }
export function _bcFfIdx(arr, ct, key) { if (!arr) return 0; let i = 0; while (i < arr.length && (arr[i][key] || 0) < ct) i++; return i; }
// Force-free a canvas's WebGL context so the GPU resources are released
// immediately instead of lingering until GC — repeated Butterchurn
// mount/unmount cycles otherwise pile up live contexts toward the browser cap.
@@ -164,7 +164,7 @@ function _bcGuitarFeed(actx, onReady) {
const BC_LS = 'viz3d_settings';
const BC_DEFAULTS = { enabled: true, opacity: 1.0, laneDim: true, laneDimStrength: 0.45, chartAccents: true, colorTint: true, chartStrength: 1.0, tintStrength: 0.65, guitarGain: 6, songGain: 1.8, cyclePool: 'all', hold: false };
let _bcSettings = null;
function _bcLoadSettings() {
export function _bcLoadSettings() {
if (_bcSettings) return _bcSettings;
let saved = {};
try { saved = JSON.parse(localStorage.getItem(BC_LS) || '{}'); } catch (e) {}
+44
View File
@@ -178,6 +178,50 @@ test('screen.js imports _bcCreateController and _bcIsDesktop from src/bc-panel.j
'screen.js must import _bcIsDesktop from ./src/bc-panel.js');
});
// ── 12. screen.js has no bare caller references to private bc-panel.js symbols ─
test('every bc-panel.js export referenced in screen.js IIFE is in the import statement', () => {
// Mutation: remove _bcLoadSettings (or any other export) from the screen.js import
// → symbol is exported by bc-panel.js, referenced in the IIFE, but not bound via
// import → ReferenceError at runtime. This is the class Creed HIGH found on
// cut 4 (screen.js:15396-15402: _bcLoadSettings + _bcFfIdx called but not imported).
//
// Method (generalised):
// exported = all _bc* symbols with `export` keyword in bc-panel.js
// imported = all symbols in screen.js's `from './src/bc-panel.js'` import clause
// leaked = exported symbols that appear as bare refs in screen.js IIFE
// but are NOT in imported
// Adding a new export and a new caller without updating the import → leaked is
// non-empty → test RED.
const bcSrc = src();
const scrSrc = screenSrc();
// All exported _bc* symbols from bc-panel.js.
const exported = new Set(
[...bcSrc.matchAll(/^export\s+(?:const|let|var|function)\s+(_bc\w+)/mg)].map(m => m[1])
);
// Symbols actually imported from bc-panel.js in screen.js.
const importMatch = scrSrc.match(/import\s+\{([^}]+)\}\s+from\s+['"]\.\/src\/bc-panel\.js['"]/);
const imported = new Set(
importMatch ? importMatch[1].split(',').map(s => s.trim()).filter(Boolean) : []
);
// IIFE body: strip import lines and line comments to avoid false positives.
const noImports = scrSrc.replace(/^import\s+.*\n/gm, '');
const noComments = noImports.replace(/\/\/[^\n]*/g, '');
// Exported symbols referenced in the IIFE body but absent from the import list.
const leaked = [...exported].filter(
sym => new RegExp('\\b' + sym + '\\b').test(noComments) && !imported.has(sym)
);
assert.deepStrictEqual(leaked, [],
'screen.js references bc-panel.js exports that are not in its import clause: ' +
leaked.join(', ') +
' — add them to the import { … } from \'./src/bc-panel.js\' line in screen.js');
});
// ── 11. screen.js IIFE no longer defines moved B-section symbols ──────────────
test('screen.js IIFE does not redeclare _bcCreateController or _bcLoadLib', () => {