fix: close checks that could pass without asserting anything, plus gallery-infrastructure debt#120
Merged
Merged
Conversation
A falsification probe on vertex-color-ao exited 0 when it should have failed: the depsgraph comparison used zip(src.data, eva.data), which truncates to the shorter sequence, so a resampled attribute read as a clean 0.0 deviation. That is a direct hit on this repository's central claim, so all 44 examples were audited for the same class of defect. Two checks could not fail at all. gltf-export-roundtrip initialised flip_err to 0.0 and derived it from a zip over two accessors whose lengths nothing asserted: deleting the entire TEXCOORD_0 attribute still exited 0. And lightmap-uv-channel asserted "zero overlapping islands" without ever establishing that its SAT detector can find one - widening SAT_EPS blinds the detector, and a blind detector reports zero. The rest are hardening. Pairings whose lengths are part of the claim now assert those lengths instead of letting zip() quietly shorten the comparison (gltf-skin-roundtrip, vertex-weight-limit, vse-cut-list). Checks built entirely from per-element loops now pin their population first, because a loop over an empty collection reports success (modular-kit-snap, socket-attach-points, vertex-color-ao). socket-attach-points additionally asserts that both branches of its up-axis rule are exercised: that assertion compared the socket basis against a re-derivation using the same threshold, so on its own it could not notice the threshold being wrong. Every guard added here was proven to fire, and all 44 checks pass unchanged on 4.5.11 LTS and 5.1.2. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
Blender's Image.save() ignores its quality argument and keeps the buffer RGBA, so the documented recipe was writing near-lossless files. Four assets shipped that way, totalling 3.95 MB; encoded from the same pixels with Pillow they total 85 KB. Whole-gallery image weight drops from 5.36 MB to 1.50 MB. Mean absolute deviation from the committed originals is about 1/255 with under 0.3% of pixels differing by more than 8/255 - ordinary q85 loss, including on the lightmap atlas board, which is the worst case here (thin saturated island strips on black). No example was re-rendered; this is an encoding change only. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
The gate printed FAIL without a magnitude, so converging into the band cost a render per guess - nine of twenty-seven renders in the last example run went on that. On-screen extent is very nearly inversely proportional to camera distance, so the measured-to-target fill ratio is a usable one-step correction, and margin shortfalls can be named per edge. Enforcement is unchanged; this is diagnostic output. The suggestion is labelled as a lower bound when the silhouette leaves the frame, because fill saturates at 1.000 there and the true extent is unknowable from the measurement. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
measure_edge90 called Vector.angle on face normals without checking length, and a zero-area face has a zero-length normal, so an asset with one lost every other measurement to a ValueError. A degenerate face is a real defect worth hearing about; the count is now reported in the aq_edge90 line and those edges are skipped rather than crashing the gate. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
The WebP recipe told authors to use a Blender call that silently ignores quality; it now specifies Pillow and gives a size sanity floor. The Playwright note recommended full-page capture as the workaround for lazy-loaded gallery images. That does not work: on the 44-card grid a fullPage capture renders every card blank even with images verified loaded. Replaced with the method that does work - force eager, scroll into view, viewport capture - and the failing advice is called out rather than quietly dropped. The asset floors get an honest status note: no floor has ever bound on a shipped asset, and one first draft passed all three and was then judged bad by eye and rebuilt. They are a filter for obvious failures; the asset sheet decides. Example counts in CLAUDE.md and AGENTS.md said 40 against a real 44, taken from the manifest. ROADMAP records that asset sheets committed before the neutral-rig camera fix used mis-scaled panels and are not comparable. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Consolidation pass. Six items, ordered by severity. Item 1 is the reason for the PR.
Runtime:
.scratch/blender-5.1.2-windows-x64/blender.exereports 5.1.2;.scratch/blender-4.5.11-windows-x64/blender.exereports 4.5.11 LTS.Everything below is a LIVE RUN unless labelled otherwise.
1 — Vacuous-check audit
Two shipped checks could not fail. Both are fixed. A third assertion was
self-referential. Everything else survived.
Method
Three passes, because re-running 44 examples' historical probes by hand would
have been guesswork about what each one once measured:
zip()oversequences whose lengths are part of the claim,
all()/any()overpossibly-empty comprehensions, loops whose only effect is a conditional
failure,
max/minwithdefault=. 19 examples carried at least one suchsite (78 total); 25 were clean. Every site was read by hand.
comparison (derived from the AST, not guessed), push it in the direction
that must break its check and require a non-zero exit. 84 of 91 mutations
broke their check. (The first run of this sweep was itself wrong — CRLF
in the job list left a stray carriage return on the direction argument, so
every run silently took the wrong branch and loosened the tolerance instead
of tightening it. Re-run after fixing.)
are inline literals rather than named constants.
Findings
gltf-export-roundtrip— could not fail.flip_errwas initialised to0.0 and derived from a
zipover two accessors whose lengths nothingasserted. Deleting the entire
TEXCOORD_0attribute:lightmap-uv-channel— could not fail. It asserted "0 overlapping UV1islands" without ever establishing that its SAT detector can find one.
Widening
SAT_EPSmakes every axis read as separated, so the detector reportszero and the check passes. Fixed with a positive control on hand-built
triangles (overlapping / disjoint / merely touching must give
True/False/False) before the scan is trusted. TheSAT_EPSmutation nowexits 7.
socket-attach-points— self-referential assertion. The up-axis rule wascompared against a re-derivation using the same
PARALLELthreshold, so bothsides moved together and collapsing the threshold changed nothing. It now
asserts that both branches are actually exercised (2 fallback / 5
Gram-Schmidt);
PARALLEL -> -1.0exits 4.Hardening — no demonstrated pass-through, but the same defect class:
length guards before
zip()ingltf-skin-roundtrip,vertex-weight-limitand
vse-cut-list'snear(); population guards inmodular-kit-snap,socket-attach-pointsandvertex-color-ao, whose checks are built entirelyfrom per-element loops that assert nothing over an empty collection.
Falsification of every guard added
gltf-export-roundtriplightmap-uv-channelsocket-attach-pointssocket-attach-pointsgltf-skin-roundtripvertex-weight-limitvse-cut-listnear()called with nothing to comparemodular-kit-snapvertex-color-aoExplained survivors (not defects)
N_NEEDLES,RINGS,POINTS,BLEND,PUMPERare construction parameters:they drive both the build and the closed form, so changing one changes both
consistently. That is correct behaviour, not a hole.
AREA_EPS/VOL_EPSindegenerate-bevel-weldandmesh-hygiene-auditare floors a measurement mustexceed, so pushing them down loosens rather than tightens.
Result
44/44 pass on 5.1.2 and 44/44 on 4.5.11 after all changes. No contract was
weakened and no measured closed form changed.
2 — WebP recipe never applied quality
Blender's
Image.save()ignoresqualityand keeps the buffer RGBA. Therecipe in
docs/VISUAL-STYLE.mdnow specifies Pillow, with a size sanityfloor.
modular-kit-snapherolightmap-uv-channelheromodular-kit-snappreviewlightmap-uv-channelpreviewWhole gallery: 5,360,979 -> 1,496,230 bytes across 88 images (-72%).
Nothing remains over 200 KB.
Fidelity against the committed originals: mean absolute deviation
0.84–1.07 / 255, max 19–29, under 0.3% of pixels differing by more than
8/255 — ordinary q85 loss. Spot-checked at full size and at card size,
including the lightmap atlas board (thin saturated strips on black, the worst
case in this set). No example was re-rendered.
3 — Framing gate gradient
framing_advicenow prints a suggested camera distance factor and per-edgemargin shortfalls. Enforcement behaviour is unchanged.
x=0.583x=1.215x=1.250 AT LEASTThe third row is why the qualifier exists: fill saturates at 1.000 once the
silhouette leaves the frame, so the ratio is only a lower bound. The gate says
so rather than implying a one-step fix it cannot deliver.
4 — Playwright guidance was wrong
The documented workaround (full-page capture, crop locally) produces blank
cards on the 44-card grid even with images verified loaded (
complete,naturalWidth 1280,opacity 1). Replaced with force-eager, scroll intoview, viewport capture — and the advice that does not work is called out
rather than quietly dropped.
5 — Asset floors: crash, and status
measure_edge90calledVector.angleon zero-length normals. Before andafter, on a manifold zero-area face produced by an over-large bevel:
degenerate_facesnow appears in theaq_edge90line. Status recorded inVISUAL-STYLE: no floor has ever bound on a shipped asset, and
socket-attach-points' first draft passed all three (edge900.000, 10materials) and was then judged bad by eye and rebuilt from scratch. The floors
are a filter for obvious failures; the asset sheet decides. No floors added or
extended.
6 — Count drift
CLAUDE.md and AGENTS.md said 40 against a real 44, taken from the
manifest. The stale "All forty run headless" phrasing is fixed too.
Also noted
ROADMAP now records that the neutral asset-sheet rig sized its camera from the
bounding-box diagonal, so sheets committed before that fix
(
modular-kit-snap,lightmap-uv-channel) contain mis-scaled panels and mustnot be cited as evidence. Not re-rendered here.
Verification
lightmap-uv-channelis the slowest at 21 s — measured 21443 ms at HEADversus 21047 ms on this branch, so it is pre-existing and untouched by the
added control. (An earlier measurement suggested a 29x regression; that was
an artefact of running the HEAD copy from
.scratch/, where itsgallery_framingimport shim cannot resolve. Diagnosed, not assumed.)source. Generated HTML read directly: new code correctly escaped (
<,"), zero raw<outside tags, index card set unchanged.390x844: 44 cards, 0 broken images, both re-encoded heroes at native
1280x720, no horizontal overflow on mobile.
Labelling
Everything above is a live run on the two binaries named at the top. The one
inference not directly executed: the 25 examples the AST scan found clean of
vacuity patterns were not each given a bespoke historical probe. They are
covered by the constant-mutation sweep and by static analysis, and all 25 pass
on both versions.