Skip to content

Commit 42bc73d

Browse files
donislawdevclaude
andcommitted
guard: the screen tolerance follows the architecture, not the system
The stored screens were compared with a tolerance keyed on darwin, because the rounding had only ever been seen on a Mac. Measuring it a second time put it somewhere else. Linux on arm64 rounds the same way, and Linux on amd64 - the same binary, the same references, the same container, the same moment - comes back identical to the byte. Two arm64 platforms round and two amd64 platforms do not, so the tolerance now asks about GOARCH. amd64 keeps exactly zero, which is the accuracy this guard actually has. The widget tree matched to the byte on all twenty five screens throughout, and it carries sizes, positions, colours and every string. So the layout is identical and only the rasterising differs, which is the case this file already had a name for. The number is held between two measurements rather than chosen. Below it, the rounding it exists to absorb. Above it, the smallest change this window can really make - one field narrowed by one pixel, which moves a channel by at least 16 and moves twenty four of the twenty five screens. Six is double the rounding and half of that. The test asks about the constants rather than about a rendering. The arm64 branch is dead code on amd64, so a test that only rendered would be green there whatever that branch said. Written first as double the measured rounding, which made the lower bound unfailable - doubling a positive number always clears it. It is its own number now, so both bounds can go red, and both were made to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 2210c6b commit 42bc73d

1 file changed

Lines changed: 97 additions & 20 deletions

File tree

‎internal/guard/screenpixels_test.go‎

Lines changed: 97 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,38 @@ import (
5656
// of this set on purpose, and saying so here is the point - a set that
5757
// quietly skipped it would read as covering everything.
5858
//
59-
// macOS, measured on the owner's Mac Mini on 2026-08-17 rather than assumed.
60-
// All eleven screens differ there, and the shape of the difference is what
61-
// decided the design: a few thousand pixels spread over the whole window, never
62-
// more than 2 of 255 in any channel. Same glyphs, same layout, same colours -
63-
// the last bit of edge blending rounds the other way.
59+
// arm64 draws the same screen a shade differently, and the tolerance below is
60+
// for that. Measured twice rather than assumed, on two different systems:
61+
// the owner's Mac Mini on 2026-08-17, and Linux in a container on 2026-08-23.
62+
// Both round the same way - a few thousand pixels spread over the whole window,
63+
// never more than 3 of 255 in any channel. Same glyphs, same layout, same
64+
// colours - the last bit of edge blending rounds the other way.
65+
//
66+
// It is the ARCHITECTURE, not the system, and that took a second measurement to
67+
// see. The first one only had a Mac, so the difference was written down as a
68+
// macOS thing and the tolerance was keyed on the operating system. Then Linux
69+
// on arm64 came back with the same rounding, and Linux on amd64 - the same
70+
// binary, the same references, the same container, the same moment - came back
71+
// identical to the byte. Two arm64 platforms round, two amd64 platforms do not.
72+
// Keying this on darwin was reading one measurement as if it were the rule.
73+
//
74+
// The strongest part of that measurement is not the pixels. The widget tree,
75+
// compared separately below, matched to the byte on all twenty five screens -
76+
// and it carries sizes, positions, colours and every string on the screen. So
77+
// the layout is identical and only the rasterising differs, which is exactly
78+
// the case this file calls "tree the same and pixels different".
6479
//
6580
// So the tolerance is on how FAR a channel moved, not on how many pixels moved,
66-
// and it applies only where that rounding was measured. The two worlds are
67-
// eight times apart: narrowing one field by a single pixel - the smallest real
68-
// change this window can make - moves a channel by 16.
81+
// and it applies only where that rounding was measured.
82+
//
83+
// What is still NOT proven, and both matter:
6984
//
70-
// What is still NOT proven: whether 2 belongs to macOS or to that one machine
71-
// and that one version of it. One Mac, measured once.
85+
// - darwin/amd64. There is no Intel Mac to measure and it is not a supported
86+
// target - release.yml says so - so this stops giving it a tolerance rather
87+
// than carrying an untested claim about it.
88+
// - WHY arm64 rounds the other way. Go allows FMA contraction on arm64 and
89+
// not on amd64, which would do it, but that is a hypothesis nobody here
90+
// measured. Named so the next person knows it is a lead, not a finding.
7291
//
7392
// To regenerate after a deliberate change:
7493
//
@@ -89,21 +108,79 @@ const (
89108
// tools/probes/guirender pins the same string for its -compare mode.
90109
const pinnedOutputDirectory = "/tfg/out"
91110

92-
// macOSBlendTolerance is how far one channel may move on macOS before this
93-
// stops calling it rounding. Measured at 2 on every screen, so this is double
94-
// that - and still four times below the 16 that the smallest real change makes.
95-
const macOSBlendTolerance = 4
111+
// measuredArmBlend is the largest channel distance arm64 was actually seen to
112+
// move: 2 of 255 on twenty four screens and 3 on preset-refused, measured
113+
// 2026-08-23 across all twenty five, and 2 on the Mac on 2026-08-17.
114+
const measuredArmBlend = 3
96115

97-
// pixelTolerance is deliberately different per system rather than uniform.
98-
// Windows and Linux were measured at exactly zero, and taking a tolerance there
99-
// would give away accuracy this guard actually has.
116+
// smallestRealChange is how far a channel moves when this window makes the
117+
// smallest change it CAN make - one field narrowed by one pixel. Measured
118+
// 2026-08-23 by doing exactly that, parts.NumericWidth from 140 to 139, and
119+
// reading what this guard reported: twenty four of the twenty five screens
120+
// moved, by at least 16, typically 16, up to 203.
121+
//
122+
// It is written down so the tolerance can be held against a measurement rather
123+
// than against a feeling, which is what the test below does.
124+
const smallestRealChange = 16
125+
126+
// armBlendTolerance is how far one channel may move on arm64 before this stops
127+
// calling it rounding. Double the measured 3, so a machine that rounds slightly
128+
// harder than the two measured ones does not go red - and still comfortably
129+
// under the 16 that the smallest real change makes.
130+
//
131+
// Written as its own number rather than as measuredArmBlend * 2, and that is
132+
// not a style choice. Derived from the measurement, it can never fall below it,
133+
// so the test below would be holding it against a bound arithmetic already
134+
// guarantees - a check that reads like one and cannot fail. It is a decision
135+
// sitting between two measurements, so it is spelled as one.
136+
const armBlendTolerance = 6
137+
138+
// pixelTolerance is deliberately different per architecture rather than
139+
// uniform. amd64 was measured at exactly zero on Windows and on Linux, and
140+
// taking a tolerance there would give away accuracy this guard actually has.
100141
func pixelTolerance() int {
101-
if runtime.GOOS == "darwin" {
102-
return macOSBlendTolerance
142+
if runtime.GOARCH == "arm64" {
143+
return armBlendTolerance
103144
}
104145
return 0
105146
}
106147

148+
// What this defends. A tolerance is the one thing in this file that can make it
149+
// pass for the wrong reason, and it can do that quietly - a number nudged up
150+
// far enough stops the pictures from ever disagreeing again, and a green run
151+
// looks the same either way.
152+
//
153+
// So the number is held against the two measurements that bracket it: the
154+
// rounding it has to absorb, and the smallest real change it must never
155+
// absorb. Both are recorded above with the date they were taken.
156+
//
157+
// This asks about the constants rather than about a rendering on purpose,
158+
// because it has to mean something on the machine it runs on. The arm64 branch
159+
// is dead code on amd64, so a test that only rendered would be green here no
160+
// matter what that branch said - which is the shape of guard this project has
161+
// been bitten by more than once.
162+
func TestTheBlendToleranceStaysBetweenTheRoundingAndTheSmallestRealChange(t *testing.T) {
163+
if armBlendTolerance < measuredArmBlend {
164+
t.Errorf("the arm64 tolerance is %d, below the %d that was actually measured.\n"+
165+
"Reason: a tolerance under the rounding it exists for makes arm64 red for edge blending.\n"+
166+
"What to do: either raise it back above the measurement or measure again and move both numbers together.",
167+
armBlendTolerance, measuredArmBlend)
168+
}
169+
if limit := smallestRealChange / 2; armBlendTolerance > limit {
170+
t.Errorf("the arm64 tolerance is %d, more than half of the %d that the smallest real change makes.\n"+
171+
"Reason: at that size it starts being able to hide a change somebody meant to see, which is the one thing this guard exists to catch.\n"+
172+
"Allowed: at most %d.\n"+
173+
"What to do: if a machine really rounds that hard, measure it and say so - do not widen this to make a run go green.",
174+
armBlendTolerance, smallestRealChange, limit)
175+
}
176+
if runtime.GOARCH != "arm64" && pixelTolerance() != 0 {
177+
t.Errorf("this is %s and the tolerance is %d rather than 0.\n"+
178+
"Reason: amd64 was measured at exactly zero on Windows and on Linux, so any tolerance here throws away accuracy this guard has.\n"+
179+
"What to do: keep the tolerance on the architecture that was measured to need it.",
180+
runtime.GOARCH, pixelTolerance())
181+
}
182+
}
183+
107184
// scene is one screen in one state, ready to be photographed.
108185
type scene struct {
109186
tab fyne.CanvasObject
@@ -627,7 +704,7 @@ func compareAgainstReference(t *testing.T, name, reference string, got image.Ima
627704
// starts climbing, the tolerance is covering something it was not
628705
// measured to cover, and nobody would see that from a green run.
629706
t.Logf("%d pixels differ by at most %d of 255, which is inside the %d allowed on %s - treated as edge blending",
630-
differing, worst, pixelTolerance(), runtime.GOOS)
707+
differing, worst, pixelTolerance(), runtime.GOARCH)
631708
return
632709
}
633710
dir := saveEvidence(t, name, wantPix, gotPix)

0 commit comments

Comments
 (0)