fix: the detail verdict never actually asked the sharpness question

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.
This commit is contained in:
digital-garbage
2026-09-04 19:20:48 +03:00
parent 38b00ff8e9
commit 6bbf8550cf
2 changed files with 51 additions and 13 deletions
+30 -1
View File
@@ -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", () => {
+21 -12
View File
@@ -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