From 6bbf8550cfeae1b949c0062ef518311ba2b0e4eb Mon Sep 17 00:00:00 2001 From: digital-garbage <133010426+digital-garbage@users.noreply.github.com> Date: Fri, 4 Sep 2026 19:20:48 +0300 Subject: [PATCH] fix: the detail verdict never actually asked the sharpness question MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dpVerdict gated on structure >= 0.85 and returned "the shot moved" otherwise — so `detail` and `edge_aligned` were never consulted. The block-repeat sweep's best row was structure 0.831, which means all 19 comparisons short-circuited and the "sharper" branch was unreachable for the entire experiment. Reporting that as "not one row read sharper" was true and misleading: sharpness was never evaluated at all. The threshold was also wrong on its own terms. Blocks 10/15/44/45 were judged indistinguishable from plain by eye and measured 0.73-0.83, so the cliff was rejecting pairs a person calls the same shot. Now: how far the shot moved is a PREFIX, not a verdict, and the detail half is always reported alongside it ("shot moved 17% · detail unchanged"), which is strictly more than either half alone. Only below 0.35 — where the two runs share almost nothing — is a detail comparison refused outright. Tested against the sweep's real rows, including a hypothetical genuine sharpen to prove the branch is now reachable. --- movie_editor/frontend/detail_verdict.test.js | 31 +++++++++++++++++- movie_editor/frontend/settings_window.js | 33 +++++++++++++------- 2 files changed, 51 insertions(+), 13 deletions(-) diff --git a/movie_editor/frontend/detail_verdict.test.js b/movie_editor/frontend/detail_verdict.test.js index 845d5ce7..2687fd26 100644 --- a/movie_editor/frontend/detail_verdict.test.js +++ b/movie_editor/frontend/detail_verdict.test.js @@ -28,6 +28,34 @@ test("every branch runs without throwing", () => { cases.forEach((r) => [null, 0.1].forEach((f) => assert.equal(typeof dpVerdict(r, f), "string"))); }); +test("a moved shot still gets a detail verdict — the old 0.85 cliff hid it", () => { + // The block-repeat sweep's best row was structure 0.831, so a >=0.85 gate returned "the + // shot moved" for all 19 comparisons and never once evaluated sharpness. Both halves get + // reported now: how much the shot moved AND what happened to the detail. + const v = dpVerdict(row({ structure: 0.831, detail: 0.99, + changed: { h3_block_repeat: ["", "40"] } }), 0); + assert.match(v, /shot moved 17%/); + assert.match(v, /detail unchanged/); +}); + +test("sharper is reachable when all three numbers agree", () => { + const v = dpVerdict(row({ structure: 0.95, detail: 1.3, edge_aligned: 0.7, + changed: { h3_block_repeat: ["", "40"] } }), 0); + assert.match(v, /SHARPER/); +}); + +test("more detail spread evenly is grain, not sharpening", () => { + const v = dpVerdict(row({ structure: 0.95, detail: 1.3, edge_aligned: 0.05, + changed: { h3_block_repeat: ["", "40"] } }), 0); + assert.match(v, /grain, not sharpening/); +}); + +test("two nearly unrelated pictures refuse a detail verdict", () => { + const v = dpVerdict(row({ structure: 0.09, detail: 1.25, + changed: { h3_block_repeat: ["", "40"] } }), 0); + assert.match(v, /detail cannot be compared/); +}); + 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%/); @@ -36,6 +64,7 @@ test("a pair with nothing changed reports the noise floor", () => { 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/); + assert.match(v, /detail unchanged/); // and the detail half survives the prefix }); test("a real prompt change voids the comparison, a small drift does not", () => { @@ -43,7 +72,7 @@ test("a real prompt change voids the comparison, a small drift does not", () => /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/); + assert.match(drift, /SHARPER/i); }); test("the floor is the median of the quiet pairs, so one outlier cannot set it", () => { diff --git a/movie_editor/frontend/settings_window.js b/movie_editor/frontend/settings_window.js index 08ed09ff..aa14aa64 100644 --- a/movie_editor/frontend/settings_window.js +++ b/movie_editor/frontend/settings_window.js @@ -443,20 +443,29 @@ 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.`; } - if (r.structure < 0.85) { - return r.same_seed - ? `the shot moved ${(moved * 100).toFixed(0)}%${vsFloor} — this changed the picture, ` - + "not just its detail" - : "different seed — two separate generations, so detail cannot be compared. Rerun " - + "the same seed with the change off, then on."; + if (r.same_seed === false) { + return "different seed — two separate generations, so detail cannot be compared. Rerun " + + "the same seed with the change off, then on."; + } + // Below this the two runs share almost nothing and the detail ratio compares unrelated + // pictures. Everything above it still gets a detail verdict, because "the shot moved AND + // the detail did not" is strictly more informative than either half alone — and the old + // 0.85 cliff swallowed the whole block-repeat sweep, whose best row was 0.831, so the + // sharpness question was never actually asked. The user's own eyes put the threshold of + // VISIBLE change near 30% moved, well past where the cliff sat. + if (r.structure < 0.35) { + return `the shot moved ${(moved * 100).toFixed(0)}%${vsFloor} — barely the same ` + + "generation, so detail cannot be compared"; } const gain = (r.detail - 1) * 100; - const caveat = r.same_seed === false ? " (different seed — treat as indicative only)" : ""; - if (Math.abs(gain) < 2) return "no real change in detail" + caveat; - if (gain < 0) return `detail DOWN ${Math.abs(gain).toFixed(0)}% — softer, not sharper` + caveat; - return (r.edge_aligned > 0.35 - ? `sharper — +${gain.toFixed(0)}% detail, landing on existing edges` - : `+${gain.toFixed(0)}% detail but spread evenly — reads as grain, not sharpening`) + caveat; + // How much the shot moved is a PREFIX, not a verdict: it says how much of the change is + // "a different shot" so the detail half can be read for what it is. + const shot = moved > 0.05 ? `shot moved ${(moved * 100).toFixed(0)}%${vsFloor} · ` : ""; + if (Math.abs(gain) < 2) return shot + "detail unchanged"; + if (gain < 0) return shot + `detail DOWN ${Math.abs(gain).toFixed(0)}% — softer`; + return shot + (r.edge_aligned > 0.35 + ? `SHARPER — +${gain.toFixed(0)}% detail, landing on existing edges` + : `+${gain.toFixed(0)}% detail but spread evenly — grain, not sharpening`); } // What this pair actually was. The probe cannot know which knob you consider the