fix: panel threw on every detail row (temporal dead zone)

`const moved` sat below the repeatability branch that uses it, so that
branch hit "can't access lexical declaration 'moved' before
initialization" the moment a row rendered. Valid syntax, so node --check
passed it — the failure only exists at runtime.

Declarations moved to the top of dpVerdict, and the panel logic now has
a test that RUNS every branch instead of parsing it. That is the actual
lesson: this file had no runtime coverage at all, and a syntax check
cannot see a TDZ.

Also: the block panel showed a cumulative count beside a green dot while
recording was off, which reads as "still recording". It now says
"Recorded earlier (not recording now)" and drops the dot. Turning
recording off has never cleared stored profiles and still doesn't — the
count is history.
This commit is contained in:
digital-garbage
2026-09-04 18:37:40 +03:00
parent a187dc055a
commit 175b08ff32
2 changed files with 70 additions and 5 deletions
@@ -0,0 +1,57 @@
// The settings panel's verdict logic, exercised rather than syntax-checked.
//
// It shipped a temporal-dead-zone throw ("can't access lexical declaration 'moved' before
// initialization") that `node --check` passed happily, because a `const` used above its own
// declaration is valid syntax and only fails when the branch actually runs. So these RUN
// every branch.
const test = require("node:test");
const assert = require("node:assert");
const fs = require("node:fs");
const src = fs.readFileSync(__dirname + "/settings_window.js", "utf8");
const grab = (name) => src.match(new RegExp(`function ${name}[\\s\\S]*?\\n \\}\\n`))[0];
const { dpVerdict, dpNoiseFloor } = new Function(
grab("dpVerdict") + grab("dpNoiseFloor") + "; return { dpVerdict, dpNoiseFloor };")();
const row = (o) => Object.assign(
{ changed: {}, same_seed: true, structure: 1, detail: 1, edge_aligned: 0, cond_shift: 0 }, o);
test("every branch runs without throwing", () => {
const cases = [
row({}),
row({ structure: 0.42, changed: { h3_repr_steering: [true, false] } }),
row({ changed: { conditioning: [null, 1] }, cond_shift: 1.0, structure: 0.5 }),
row({ changed: { h3_block_repeat: ["", "40"] }, structure: 0.95, detail: 1.3, edge_aligned: 0.7 }),
row({ same_seed: false, changed: { seed: [1, 2] }, structure: 0.3 }),
row({ structure: 0.95, detail: 0.8, changed: { cfg: [1, 2] } }),
];
cases.forEach((r) => [null, 0.1].forEach((f) => assert.equal(typeof dpVerdict(r, f), "string")));
});
test("a pair with nothing changed reports the noise floor", () => {
const v = dpVerdict(row({ structure: 0.9 }), 0.1);
assert.match(v, /noise floor: two identical runs differ by 10%/);
});
test("movement is expressed against the measured floor", () => {
const v = dpVerdict(row({ structure: 0.42, changed: { h3_repr_steering: [true, false] } }), 0.1);
assert.match(v, /5\.8x your noise floor/);
});
test("a real prompt change voids the comparison, a small drift does not", () => {
assert.match(dpVerdict(row({ changed: { conditioning: [null, 1] }, cond_shift: 1.0, structure: 0.5 }), 0.1),
/prompt or scenes changed/);
const drift = dpVerdict(row({ changed: { conditioning: [null, 0.02] }, cond_shift: 0.02,
structure: 0.95, detail: 1.3, edge_aligned: 0.7 }), 0.1);
assert.match(drift, /sharper/);
});
test("the floor is the median of the quiet pairs, so one outlier cannot set it", () => {
const rows = [row({ structure: 0.9 }), row({ structure: 0.91 }), row({ structure: 0.2 }),
row({ structure: 0.5, changed: { cfg: [1, 2] } })];
assert.ok(Math.abs(dpNoiseFloor(rows) - 0.1) < 0.02);
});
test("no quiet pairs means no floor rather than a made-up one", () => {
assert.equal(dpNoiseFloor([row({ changed: { cfg: [1, 2] } })]), null);
});
+13 -5
View File
@@ -421,6 +421,12 @@
}
function dpVerdict(r, floor) {
// Declared FIRST: every branch below can reach for these, and a `const` used above its
// own declaration is a temporal-dead-zone throw at runtime that a syntax check happily
// passes ("can't access lexical declaration 'moved' before initialization").
const moved = 1 - r.structure;
const vsFloor = floor && floor > 1e-6 ? ` — ${(moved / floor).toFixed(1)}x your noise floor`
: "";
// A low `structure` has two very different causes and the numbers cannot separate them,
// so the seed decides. Different seeds = two different generations and the comparison
// never was an A/B (a reference pins the subject, not the sample). Same seed = the
@@ -437,9 +443,6 @@
return `noise floor: two identical runs differ by ${(moved * 100).toFixed(0)}% `
+ `(detail x${r.detail.toFixed(2)}). Anything smaller than this is not a result.`;
}
const moved = 1 - r.structure;
const vsFloor = floor && floor > 1e-6 ? ` — ${(moved / floor).toFixed(1)}x your noise floor`
: "";
if (r.structure < 0.85) {
return r.same_seed
? `the shot moved ${(moved * 100).toFixed(0)}%${vsFloor} — this changed the picture, `
@@ -642,9 +645,14 @@
row.append(lbl);
rows.append(row);
rows.append(infoRow("Rated generations recorded (this key)",
// The count is HISTORY. Turning recording off does not clear it, and a bare number
// beside a green dot reads as "still recording" — say which it is.
const recording = !!(biState && biState.enabled);
rows.append(infoRow(
recording ? "Rated generations recorded (this key)"
: "Recorded earlier (not recording now)",
biState ? `${runs} (${biState.n_liked} liked / ${biState.n_disliked} disliked)`
: "0", runs > 0 ? true : null));
: "0", recording && runs > 0 ? true : null));
rows.append(actionRow("Save this key's measurement",
"Downloads every recorded profile as one file. A rental gets replaced and "
+ "refinements/ is not in git, so without this the count restarts on the next box.",