port: run the peer's shift-vs-blur discriminator -- the menu is not misregistered
They proposed the test and said they had not run it. It needs the port's render
beside the capture, so it was mine: a shift makes the signed residual track the
gradient (and the fitted slope is the shift in pixels), a blur makes it track the
laplacian. New tool, tools/port/edge-residual-kind.
Controls first, and they are not optional: a known +1 px shift reads back as
+0.938 px (r +0.789), a known blur as r -0.896 on the laplacian, neither leaking
into the other's channel.
Result: -0.010 px, -0.009 px, laplacian r +0.103. Flat. Any whole-frame
translation is under a hundredth of a pixel, and the weak blur term has the
opposite sign to the blur control. Misregistration is excluded.
Reach: whole-frame fit. One misplaced element is a small share of 38 752 edge
pixels and would not move these numbers -- this excludes a GLOBAL translation,
not a local one. A misplaced soft element remains untouched.
Exit codes 0 or 2, no 1 -- it classifies, it does not judge. The control-failure
branch verified by raising the thresholds: report suppressed, exit 2.
Also: GP_DIALOG 2/3 restored to authored/flow.json. I declined to re-add it on a
relay; they have now taken the ja capture (HANDOFF at 5a7f34d) -- 1.82 % of pixels
in four bands, EASY/NORMAL/HARD not among them because JP keeps Latin script. My
objection stands as an objection: adjacency never implied a language pair, and 26
of 65 adjacent pairs differ in button count. The claim now rests on a capture
instead of on that inference.
This commit is contained in:
@@ -327,6 +327,25 @@
|
||||
"zero rivals disc-wide, plus the oracle capture. The pairing was decoration on a",
|
||||
"conclusion that stands without it, which is exactly why it travelled unchecked.",
|
||||
"",
|
||||
"✅ RESTORED 2026-08-31, ON A MEASUREMENT RATHER THAN A RELAY. The Decoder took",
|
||||
"the `ja` capture of DIFFICULTY that was missing and 2/3 ARE English/Japanese:",
|
||||
"EN vs JP differ in 1.82 % of pixels in FOUR BANDS AND NOWHERE ELSE -- the",
|
||||
"heading (DIFFICULTY -> the JP heading), the ring by 2 px, the BACK label, and",
|
||||
"the footer. EASY/NORMAL/HARD are NOT in the differing set: the Japanese release",
|
||||
"leaves the three difficulty names in Latin script, which is why the disc figure",
|
||||
"is only 2.77 % of bytes against 1.82 % of pixels.",
|
||||
"",
|
||||
"📌 MY OBJECTION WAS NOT WRONG AND IS NOT WITHDRAWN. It was that IDENTICAL",
|
||||
"ELEMENT SETS DO NOT IMPLY A LANGUAGE PAIR -- 26 of 65 adjacent pairs differ in",
|
||||
"button count, so adjacency proves nothing. That argument still holds; what has",
|
||||
"changed is that the conclusion now rests on a direct locale capture instead of",
|
||||
"on that inference. A bad argument for a true claim is still a bad argument, and",
|
||||
"the claim was correctly out of this file until somebody went and looked.",
|
||||
"",
|
||||
"⚠️ REACH, THEIRS: one JP boot, one screen, does not generalise. GP_TITLE 4/7 is",
|
||||
"known to differ by MORE than text -- entry 7 carries nine sprites entry 4 lacks.",
|
||||
"Nothing in the port keys off locale today; this is recorded, not consumed.",
|
||||
"",
|
||||
"🔴 RECORD LAYOUT CORRECTED 2026-09-01, and I had copied the wrong one. I wrote",
|
||||
"\"(handler, id, name pointer)\"; it is {id, name_ptr, handler} -- the same three",
|
||||
"fields shifted one word, so every record was being credited with the PREVIOUS",
|
||||
|
||||
@@ -9,7 +9,7 @@ dies, which is what this file is for.
|
||||
|
||||
<!-- INDEX: generated by tools/port/index-decisions -- do not hand-edit -->
|
||||
|
||||
323 sections. Search this before re-deriving anything.
|
||||
325 sections. Search this before re-deriving anything.
|
||||
|
||||
* [P0 — the exporter, 2026-08-28](#p0--the-exporter-2026-08-28)
|
||||
* [P1 — Godot draws the screen, 2026-08-28](#p1--godot-draws-the-screen-2026-08-28)
|
||||
@@ -334,6 +334,8 @@ dies, which is what this file is for.
|
||||
* [A workflow defect of mine, on its fourth occurrence](#a-workflow-defect-of-mine-on-its-fourth-occurrence)
|
||||
* [The menu residual, decomposed — and half of 13.06 is tone](#the-menu-residual-decomposed--and-half-of-1306-is-tone)
|
||||
* [Refutation: the peer's tone/geometry positive control rests on a number of mine that cannot carry it](#refutation-the-peers-tonegeometry-positive-control-rests-on-a-number-of-mine-that-cannot-carry-it)
|
||||
* [The menu's edge residual is **not** a misregistration — the Decoder's discriminator, run](#the-menus-edge-residual-is-not-a-misregistration--the-decoders-discriminator-run)
|
||||
* [`GP_DIALOG` 2/3 restored to `authored/flow.json` — on a measurement this time](#gp_dialog-23-restored-to-authoredflowjson--on-a-measurement-this-time)
|
||||
|
||||
<!-- /INDEX -->
|
||||
## P0 — the exporter, 2026-08-28
|
||||
@@ -15727,3 +15729,72 @@ edge concentration is spatial. The menu's is 3.2×.
|
||||
concentration is the signature of sub-pixel misregistration, of antialiasing, or
|
||||
of a genuinely misplaced soft element, and this measurement does not separate
|
||||
those three. I am not guessing between them.
|
||||
|
||||
## The menu's edge residual is **not** a misregistration — the Decoder's discriminator, run
|
||||
|
||||
They proposed the test and said plainly they had not run it: *"the discriminator
|
||||
would be whether the edge excess is signed — a misplaced element gives a residual
|
||||
with a consistent direction along the edge, antialiasing does not. That is a real
|
||||
experiment and I have not run it, so it is a route, not an answer."*
|
||||
|
||||
📌 **It needed my render beside the capture, so it was mine to run.** Made concrete:
|
||||
a shift by `(dx,dy)` makes the signed residual track the **gradient**, and the
|
||||
fitted slope *is the shift in pixels*; a blur makes it track the **Laplacian**,
|
||||
symmetric and directionless. New tool: `tools/port/edge-residual-kind`.
|
||||
|
||||
### The controls come first, and they are not a flag
|
||||
|
||||
| render vs a deliberately damaged copy of itself | d/dx | d/dy | laplacian |
|
||||
|---|---|---|---|
|
||||
| known **+1 px horizontal shift** | **r +0.789, slope +0.938 px** | −0.008 px | −0.306 |
|
||||
| known **blur, no shift** | +0.003 px | +0.004 px | **r −0.896** |
|
||||
|
||||
✅ **A 1-px shift reads back as +0.938 px.** The instrument recovers both what it
|
||||
is for, and neither control leaks into the other's channel.
|
||||
|
||||
### The result
|
||||
|
||||
| `main_menu` render vs oracle capture | value |
|
||||
|---|---|
|
||||
| horizontal shift | r −0.027, **slope −0.010 px** |
|
||||
| vertical shift | r −0.027, **slope −0.009 px** |
|
||||
| blur / sharpness | r +0.103 |
|
||||
|
||||
🔴 **Flat on all three. The menu is not globally misregistered** — any whole-frame
|
||||
translation is under **a hundredth of a pixel**, against a control that reads a
|
||||
true 1 px at 0.938. That excludes the most worrying of the three candidates, and
|
||||
the one a renderer can silently acquire. The blur channel at +0.103 is weak *and
|
||||
the opposite sign to the blur control*, so the capture is not a softened render
|
||||
either.
|
||||
|
||||
⚠️ **Reach, and it is the whole reach:** this is a **whole-frame** fit. A single
|
||||
misplaced element is a small share of 38 752 edge pixels and would not move these
|
||||
numbers. **This excludes a global translation, not a local one.** Of the peer's
|
||||
three candidates it kills misregistration and weakens uniform antialiasing; a
|
||||
misplaced soft element is untouched by it, and I am not claiming otherwise.
|
||||
|
||||
📌 Exit codes are **0 or 2, and there is no 1** — the tool classifies, it does not
|
||||
judge. If either control fails it prints nothing but the failure: verified by
|
||||
raising the thresholds to 0.99, which suppresses the report and exits 2. A guard
|
||||
nobody has watched fail is decoration.
|
||||
|
||||
## `GP_DIALOG` 2/3 restored to `authored/flow.json` — on a measurement this time
|
||||
|
||||
I withdrew *"an EN/JP pair"* as an unchecked relay and **declined to re-add it**
|
||||
because nothing depended on it. The Decoder has now taken the `ja` capture that
|
||||
was missing (`HANDOFF` at `5a7f34d`, dated today): EN and JP differ in **1.82 % of
|
||||
pixels in four bands and nowhere else** — heading, the ring by 2 px, `BACK`, the
|
||||
footer. `EASY`/`NORMAL`/`HARD` are *not* in the differing set; the Japanese release
|
||||
leaves them in Latin script, which is why the disc figure is only **2.77 % of
|
||||
bytes**.
|
||||
|
||||
📌 **My objection was not wrong and is not withdrawn.** It was that *identical
|
||||
element sets do not imply a language pair* — 26 of 65 adjacent `GP_DIALOG` pairs
|
||||
differ in button count, so adjacency proves nothing. That still holds. What changed
|
||||
is that the claim now rests on a direct locale capture instead of on that
|
||||
inference. **A bad argument for a true claim is still a bad argument**, and the
|
||||
claim was correctly out of the file until somebody went and looked.
|
||||
|
||||
⚠️ Their reach, carried across: one JP boot, one screen, does not generalise —
|
||||
`GP_TITLE` 4/7 differs by more than text. Nothing in the port keys off locale
|
||||
today, so this is recorded, not consumed.
|
||||
|
||||
138
tools/port/edge-residual-kind
Executable file
138
tools/port/edge-residual-kind
Executable file
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""What KIND of error is left at the edges after tone is accounted for?
|
||||
|
||||
tools/port/edge-residual-kind [screen] # default main_menu
|
||||
|
||||
`verify-capture`'s `diff` column thresholds at 25 % and so only sees GROSS
|
||||
displacement. Fitting a per-level LUT removes everything a tone effect can
|
||||
explain. What is left on the main menu is concentrated 3.2x on edge pixels
|
||||
(DECISIONS.md, 2026-08-31) -- and three things produce that: a misregistration,
|
||||
an antialiasing difference, or a genuinely misplaced element.
|
||||
|
||||
THE DISCRIMINATOR IS THE SIGN, and it is the Decoder's, from their reply on
|
||||
2026-08-31: a shift gives a residual with a CONSISTENT DIRECTION along the edge,
|
||||
an antialiasing difference does not. Made concrete:
|
||||
|
||||
* shifted by (dx,dy): residual ~ dx*d/dx + dy*d/dy -- and the fitted SLOPE
|
||||
IS THE SHIFT IN PIXELS
|
||||
* blurred/sharpened : residual ~ -k * laplacian -- symmetric, no direction
|
||||
|
||||
EXIT CODES. 0 the report is trustworthy, 2 A CONTROL FAILED so the numbers below
|
||||
it mean nothing. There is no 1: this tool classifies, it does not judge. A
|
||||
correlation this tool reports is worthless without the two controls above it,
|
||||
which is why they are not optional and not a flag.
|
||||
"""
|
||||
import math, os, subprocess, sys, tempfile
|
||||
|
||||
CAPS = "docs/re/captures/title-builds"
|
||||
SCREEN = sys.argv[1] if len(sys.argv) > 1 else "main_menu"
|
||||
# The captures are a 1279x675 top-left crop of the 1280x720 guest surface, so the
|
||||
# render is cropped to match and NOTHING IS SCALED -- resampling would manufacture
|
||||
# exactly the edge signal this tool measures. See verify-capture, same reason.
|
||||
W, H = 1279, 675
|
||||
EDGE = 12 # |grad| above which a pixel is an edge
|
||||
PASS_SHIFT, PASS_BLUR = 0.70, -0.70
|
||||
|
||||
|
||||
def gray(png, out):
|
||||
subprocess.run(["convert", png, "-colorspace", "Gray", "-depth", "8",
|
||||
"gray:" + out], check=True)
|
||||
return open(out, "rb").read()
|
||||
|
||||
|
||||
def lutfit(a, b):
|
||||
tot = [0] * 256; cnt = [0] * 256
|
||||
for i in range(len(a)):
|
||||
tot[a[i]] += b[i]; cnt[a[i]] += 1
|
||||
return [(tot[v] // cnt[v]) if cnt[v] else v for v in range(256)]
|
||||
|
||||
|
||||
def analyse(a, b):
|
||||
lut = lutfit(a, b)
|
||||
gx = []; gy = []; lp = []; rs = []
|
||||
for y in range(1, H - 1):
|
||||
o = y * W
|
||||
for x in range(1, W - 1):
|
||||
i = o + x
|
||||
ax = (a[i + 1] - a[i - 1]) * 0.5
|
||||
ay = (a[i + W] - a[i - W]) * 0.5
|
||||
if abs(ax) + abs(ay) < EDGE:
|
||||
continue
|
||||
gx.append(ax); gy.append(ay)
|
||||
lp.append(float(a[i + 1] + a[i - 1] + a[i + W] + a[i - W] - 4 * a[i]))
|
||||
rs.append(float(lut[a[i]] - b[i]))
|
||||
n = len(rs)
|
||||
if n < 1000:
|
||||
print(f" 🔴 only {n} edge pixels -- nothing to classify"); sys.exit(2)
|
||||
mr = sum(rs) / n
|
||||
|
||||
def fit(u):
|
||||
mu = sum(u) / n
|
||||
suu = sum((v - mu) ** 2 for v in u)
|
||||
srr = sum((v - mr) ** 2 for v in rs)
|
||||
sur = sum((u[k] - mu) * (rs[k] - mr) for k in range(n))
|
||||
return (0.0, 0.0) if suu <= 0 or srr <= 0 else (sur / suu, sur / math.sqrt(suu * srr))
|
||||
return n, fit(gx), fit(gy), fit(lp)
|
||||
|
||||
|
||||
def row(label, res):
|
||||
n, (sx, rx), (sy, ry), (sl, rl) = res
|
||||
print(f" {label} (n={n})")
|
||||
print(f" horizontal shift : r={rx:+.3f} slope={sx:+.3f} px")
|
||||
print(f" vertical shift : r={ry:+.3f} slope={sy:+.3f} px")
|
||||
print(f" blur / sharpness : r={rl:+.3f} coef ={sl:+.3f}")
|
||||
return rx, ry, rl
|
||||
|
||||
|
||||
def shifted(a, dx):
|
||||
out = bytearray(a)
|
||||
for y in range(H):
|
||||
for x in range(W):
|
||||
out[y * W + x] = a[y * W + min(W - 1, max(0, x - dx))]
|
||||
return bytes(out)
|
||||
|
||||
|
||||
def blurred(a):
|
||||
out = bytearray(a)
|
||||
for y in range(1, H - 1):
|
||||
o = y * W
|
||||
for x in range(1, W - 1):
|
||||
i = o + x
|
||||
out[i] = (a[i] * 4 + a[i + 1] + a[i - 1] + a[i + W] + a[i - W]) // 8
|
||||
return bytes(out)
|
||||
|
||||
|
||||
tmp = tempfile.mkdtemp()
|
||||
cap_png = f"{CAPS}/live-{SCREEN.replace('_', '-')}.png"
|
||||
if not os.path.exists(cap_png):
|
||||
print(f" 🔴 no capture: {cap_png}"); sys.exit(2)
|
||||
render = os.environ.get("RENDER") or f"{tmp}/render.png"
|
||||
if not os.path.exists(render):
|
||||
print(f" 🔴 no render at {render} -- set RENDER=<png>"); sys.exit(2)
|
||||
subprocess.run(["convert", render, "-crop", f"{W}x{H}+0+0", "+repage",
|
||||
f"{tmp}/crop.png"], check=True)
|
||||
r = gray(f"{tmp}/crop.png", f"{tmp}/r.gray")
|
||||
c = gray(cap_png, f"{tmp}/c.gray")
|
||||
|
||||
print("CONTROLS -- the render against a deliberately damaged copy of itself.")
|
||||
print("A correlation below is meaningless unless these two recover what was done.\n")
|
||||
ra = analyse(r, shifted(r, 1))
|
||||
rxa, _, rla = row("known +1 px HORIZONTAL shift", ra)
|
||||
rb = analyse(r, blurred(r))
|
||||
_, _, rlb = row("known BLUR, no shift", rb)
|
||||
bad = []
|
||||
if rxa < PASS_SHIFT: bad.append(f"shift control r={rxa:+.3f} < {PASS_SHIFT}")
|
||||
if rlb > PASS_BLUR: bad.append(f"blur control r={rlb:+.3f} > {PASS_BLUR}")
|
||||
if bad:
|
||||
print("\n 🔴 CONTROL FAILED: " + "; ".join(bad))
|
||||
print(" The discriminator cannot see what it is for. Report suppressed.")
|
||||
sys.exit(2)
|
||||
print(f"\n ✅ controls pass -- a 1 px shift reads as {ra[1][0]:+.3f} px\n")
|
||||
print(f"THE REAL PAIR -- {SCREEN}\n")
|
||||
rx, ry, rl = row(f"{SCREEN} render vs oracle capture", analyse(r, c))
|
||||
print()
|
||||
if max(abs(rx), abs(ry)) < 0.15 and abs(rl) < 0.3:
|
||||
print(" => NEITHER a global shift NOR a uniform blur.")
|
||||
print(" ⚠️ REACH: this is a WHOLE-FRAME fit. One misplaced element is a small")
|
||||
print(" share of the edge pixels and would not move these numbers. This")
|
||||
print(" excludes a global translation; it does not exclude a local one.")
|
||||
Reference in New Issue
Block a user